diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh index 3c89fd3e5a..23a16f4319 100644 --- a/.ci/build-mac-arm64.sh +++ b/.ci/build-mac-arm64.sh @@ -72,13 +72,13 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.8.1 URL workaround + # nested Qt 6.8.2 URL workaround # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader cd "/tmp/Qt" "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.1 workaround + # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.2 workaround "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" fi diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index e6ef1a0178..cdc175c200 100644 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -39,13 +39,13 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.8.1 URL workaround + # nested Qt 6.8.2 URL workaround # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader cd "/tmp/Qt" "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.1 workaround + # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.2 workaround "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" fi diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh index 7a7522f8d4..e109dee9e1 100755 --- a/.ci/deploy-windows.sh +++ b/.ci/deploy-windows.sh @@ -1,5 +1,8 @@ #!/bin/sh -ex +# First let's see print some info about our caches +"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v + # BUILD_blablabla is Azure specific, so we wrap it for portability ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index a8fcec17d4..789253d9d5 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -22,6 +22,7 @@ QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-19.1.7/llvmlibs_mt.7z' GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z' VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-1.3.268.0-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1" +CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.10.2/ccache-4.10.2-windows-x86_64.zip" DEP_URLS=" \ $QT_BASE_URL \ @@ -31,10 +32,11 @@ DEP_URLS=" \ $QT_SVG_URL \ $LLVMLIBS_URL \ $GLSLANG_URL \ - $VULKAN_SDK_URL" + $VULKAN_SDK_URL\ + $CCACHE_URL" # Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually -[ -d "$CACHE_DIR" ] || mkdir "$CACHE_DIR" +[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR" # Pull all the submodules except llvm, since it is built separately and we just download that build # Note: Tried to use git submodule status, but it takes over 20 seconds @@ -58,10 +60,9 @@ download_and_verify() fileName="$4" for _ in 1 2 3; do - [ -e "$CACHE_DIR/$fileName" ] || curl -fLo "$CACHE_DIR/$fileName" "$url" - fileChecksum=$("${algo}sum" "$CACHE_DIR/$fileName" | awk '{ print $1 }') + [ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url" + fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }') [ "$fileChecksum" = "$correctChecksum" ] && return 0 - rm "$CACHE_DIR/$fileName" done return 1; @@ -80,11 +81,12 @@ for url in $DEP_URLS; do *qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;; *llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./build/lib_ext/Release-x64" ;; *glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./build/lib_ext/Release-x64" ;; + *ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;; *Vulkan*) # Vulkan setup needs to be run in batch environment # Need to subshell this or else it doesn't wait download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName" - cp "$CACHE_DIR/$fileName" . + cp "$DEPS_CACHE_DIR/$fileName" . _=$(echo "$fileName --accept-licenses --default-answer --confirm-command install" | cmd) continue ;; @@ -92,9 +94,15 @@ for url in $DEP_URLS; do esac download_and_verify "$url" "$checksum" "$algo" "$fileName" - 7z x -y "$CACHE_DIR/$fileName" -aos -o"$outDir" + 7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir" done +# Setup ccache tool +[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")" +CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR") +mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR" +cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe + # Gather explicit version number and number of commits COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) COMM_COUNT=$(git rev-list --count HEAD) diff --git a/.cirrus.yml b/.cirrus.yml index bc00884a48..2ef3e9d7ed 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,7 +7,7 @@ env: BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH RPCS3_TOKEN: ENCRYPTED[100ebb8e3552bf2021d0ef55dccda3e58d27be5b6cab0b0b92843ef490195d3c4edaefa087e4a3b425caa6392300b9b1] QT_VER_MAIN: '6' - QT_VER: '6.8.1' + QT_VER: '6.8.2' # windows_task: # matrix: @@ -21,7 +21,7 @@ env: # COMPILER: msvc # BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\ # QT_VER_MSVC: 'msvc2022' -# QT_DATE: '202411221531' +# QT_DATE: '202501260838' # QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 # VULKAN_VER: '1.3.268.0' # VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' @@ -56,7 +56,7 @@ env: # linux_task: # container: -# image: rpcs3/rpcs3-ci-jammy:1.0 +# image: rpcs3/rpcs3-ci-jammy:1.1 # cpu: 4 # memory: 16G # env: @@ -114,38 +114,38 @@ freebsd_task: install_script: "sh -ex ./.ci/install-freebsd.sh" script: "./.ci/build-freebsd.sh" -linux_aarch64_task: - env: - BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts - ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/ - CCACHE_DIR: "/tmp/ccache_dir" - CCACHE_MAXSIZE: 300M - CI_HAS_ARTIFACTS: true - UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 - UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" - DEPLOY_APPIMAGE: true - APPDIR: "./appdir" - RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" - COMPILER: clang - ccache_cache: - folder: "/tmp/ccache_dir" - matrix: - - name: Cirrus Linux AArch64 Clang - arm_container: - image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.0' - cpu: 8 - memory: 8G - clang_script: - - mkdir artifacts - - "sh -ex ./.ci/build-linux-aarch64.sh" - artifacts: - name: Artifact - path: "artifacts/*" - push_script: | - if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then - COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) - COMM_COUNT=$(git rev-list --count HEAD) - COMM_HASH=$(git rev-parse --short=8 HEAD) - export AVVER="${COMM_TAG}-${COMM_COUNT}" - .ci/github-upload.sh - fi; +# linux_aarch64_task: +# env: +# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts +# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/ +# CCACHE_DIR: "/tmp/ccache_dir" +# CCACHE_MAXSIZE: 300M +# CI_HAS_ARTIFACTS: true +# UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 +# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" +# DEPLOY_APPIMAGE: true +# APPDIR: "./appdir" +# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" +# COMPILER: clang +# ccache_cache: +# folder: "/tmp/ccache_dir" +# matrix: +# - name: Cirrus Linux AArch64 Clang +# arm_container: +# image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.1' +# cpu: 8 +# memory: 8G +# clang_script: +# - mkdir artifacts +# - "sh -ex ./.ci/build-linux-aarch64.sh" +# artifacts: +# name: Artifact +# path: "artifacts/*" +# push_script: | +# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then +# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) +# COMM_COUNT=$(git rev-list --count HEAD) +# COMM_HASH=$(git rev-parse --short=8 HEAD) +# export AVVER="${COMM_TAG}-${COMM_COUNT}" +# .ci/github-upload.sh +# fi; diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 76619d766b..789e923075 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -20,7 +20,8 @@ concurrency: env: BUILD_REPOSITORY_NAME: ${{ github.repository }} BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }} - BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/build + BUILD_SOURCEVERSION: ${{ github.sha }} + BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/ jobs: Linux_Build: @@ -29,17 +30,20 @@ jobs: matrix: include: - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.0" + docker_img: "rpcs3/rpcs3-ci-jammy:1.1" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: clang - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.0" + docker_img: "rpcs3/rpcs3-ci-jammy:1.1" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: gcc - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.0" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.1" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: clang + UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 + UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" + name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} runs-on: ${{ matrix.os }} env: CCACHE_DIR: ${{ github.workspace }}/ccache @@ -49,7 +53,8 @@ jobs: ARTDIR: "/root/artifacts" RELEASE_MESSAGE: "/rpcs3/GitHubReleaseMessage.txt" COMPILER: ${{ matrix.compiler }} - + UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }} + UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }} steps: - name: Checkout repository uses: actions/checkout@main @@ -60,7 +65,9 @@ jobs: uses: actions/cache@main with: path: ${{ env.CCACHE_DIR }} - key: ${{ runner.os }}-ccache-${{ matrix.compiler }} + key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}} + restore-keys: | + ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- - name: Docker setup and build run: | @@ -74,30 +81,45 @@ jobs: ${{ matrix.build_sh }} - name: Upload artifacts - #TODO: Upload artifact to release repository - #condition for release - #if: | - # github.event_name != 'pull_request' && - # github.repository == 'RPCS3/rpcs3' && - # github.ref == 'refs/heads/master' && - # matrix.compiler == 'clang' uses: actions/upload-artifact@main with: name: RPCS3 for Linux (${{ runner.arch }}, ${{ matrix.compiler }}) path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}/*.AppImage compression-level: 0 + + - name: Deploy master build to GitHub Releases + if: | + github.event_name != 'pull_request' && + github.repository == 'RPCS3/rpcs3' && + github.ref == 'refs/heads/master' && + matrix.compiler == 'clang' && + runner.arch == 'ARM64' + env: + RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }} + run: | + COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) + COMM_COUNT=$(git rev-list --count HEAD) + COMM_HASH=$(git rev-parse --short=8 HEAD) + export AVVER="${COMM_TAG}-${COMM_COUNT}" + .ci/github-upload.sh Windows_Build: + name: RPCS3 Windows runs-on: windows-2025 env: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.8.1' + QT_VER: '6.8.2' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202411221531' + QT_DATE: '202501260838' VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' - CACHE_DIR: ./cache + CCACHE_SHA: '6252f081876a9a9f700fae13a5aec5d0d486b28261d7f1f72ac11c7ad9df4da9' + CCACHE_BIN_DIR: 'C:\ccache_bin' + CCACHE_DIR: 'C:\ccache' + CCACHE_INODECACHE: 'true' + CCACHE_SLOPPINESS: 'time_macros' + DEPS_CACHE_DIR: ./dependency_cache steps: - name: Checkout repository @@ -114,12 +136,19 @@ jobs: shell: bash run: .ci/get_keys-windows.sh - - name: Setup Cache + - name: Setup Build Ccache uses: actions/cache@main with: - path: ${{ env.CACHE_DIR }} - key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}" - restore-keys: ${{ runner.os }}-${{ env.COMPILER }} + path: ${{ env.CCACHE_DIR }} + key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}" + restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}- + + - name: Setup Dependencies Cache + uses: actions/cache@main + with: + path: ${{ env.DEPS_CACHE_DIR }} + key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}" + restore-keys: ${{ runner.os }}-${{ env.COMPILER }}- - name: Download and unpack dependencies shell: bash @@ -138,12 +167,12 @@ jobs: uses: microsoft/setup-msbuild@main - name: Compile RPCS3 - run: msbuild rpcs3.sln /p:Configuration=Release /p:Platform=x64 + run: msbuild rpcs3.sln /p:Configuration=Release /p:Platform=x64 /p:CLToolPath=${{ env.CCACHE_BIN_DIR }} /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="${{ github.workspace }}\buildfiles\msvc\ci_no_debug_info.targets" - name: Pack up build artifacts shell: bash run: | - mkdir -p ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} + mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}" .ci/deploy-windows.sh - name: Upload artifacts (7z) @@ -156,10 +185,6 @@ jobs: uses: actions/upload-artifact@main with: name: RPCS3 for Windows (MSVC) - # 7z - # 7z.sha256 - path: | - ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}/${{ env.BUILD }} - ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}/${{ env.BUILD }}.sha256 + path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} compression-level: 0 if-no-files-found: error diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index 34cf9d54a4..57495c6487 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit 34cf9d54a46598c44938aa7598820484d7af7133 +Subproject commit 57495c64871d18905a0941db9196ef90bafe9a29 diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index 0024abd279..872555f4ba 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit 0024abd279d3a06435c0309a3f4172eed7c7a19a +Subproject commit 872555f4ba910252783af1507f9e7fe1653be252 diff --git a/3rdparty/libpng/libpng.vcxproj b/3rdparty/libpng/libpng.vcxproj index 189db8e307..7e781065a6 100644 --- a/3rdparty/libpng/libpng.vcxproj +++ b/3rdparty/libpng/libpng.vcxproj @@ -68,7 +68,6 @@ false false pngpriv.h - true CompileAsC true $(DisableSpecificWarnings) @@ -91,7 +90,6 @@ false false pngpriv.h - true CompileAsC true false diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index 8236e01a9f..7a44b1ab00 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 8236e01a9f758d15927624925c6043f84d8a261f +Subproject commit 7a44b1ab002cee6efa56d3b4c0e146b7fbaed80b diff --git a/3rdparty/opencv/CMakeLists.txt b/3rdparty/opencv/CMakeLists.txt index 2071caa5b6..1103f3be94 100644 --- a/3rdparty/opencv/CMakeLists.txt +++ b/3rdparty/opencv/CMakeLists.txt @@ -4,7 +4,7 @@ set(OPENCV_TARGET 3rdparty_dummy_lib PARENT_SCOPE) if (USE_SYSTEM_OPENCV) message(STATUS "RPCS3: using system OpenCV") - find_package(OpenCV) + find_package(OpenCV COMPONENTS core photo) if(OPENCV_FOUND) message(STATUS "RPCS3: found system OpenCV") diff --git a/3rdparty/zlib/zlib.vcxproj b/3rdparty/zlib/zlib.vcxproj index f0daafdd14..dbc63d51a2 100644 --- a/3rdparty/zlib/zlib.vcxproj +++ b/3rdparty/zlib/zlib.vcxproj @@ -84,7 +84,6 @@ $(WarningLevel) ProgramDatabase Disabled - true true $(DisableSpecificWarnings);4127;4131;4242;4244 $(TreatWarningAsError) @@ -102,7 +101,6 @@ true true false - true true $(DisableSpecificWarnings);4127;4131;4242;4244 $(TreatWarningAsError) diff --git a/3rdparty/zstd/zstd b/3rdparty/zstd/zstd index 97291fc502..f8745da6ff 160000 --- a/3rdparty/zstd/zstd +++ b/3rdparty/zstd/zstd @@ -1 +1 @@ -Subproject commit 97291fc5020a8994019ab76cf0cda83a9824374c +Subproject commit f8745da6ff1ad1e7bab384bd1f9d742439278e99 diff --git a/3rdparty/zstd/zstd.vcxproj b/3rdparty/zstd/zstd.vcxproj index 8527d72ca1..720fabe962 100644 --- a/3rdparty/zstd/zstd.vcxproj +++ b/3rdparty/zstd/zstd.vcxproj @@ -58,6 +58,7 @@ + @@ -97,6 +98,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/BUILDING.md b/BUILDING.md index 48289878ad..7cad89f8c5 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -19,26 +19,26 @@ The following tools are required to build RPCS3 on Windows 10 or later: with standalone **CMake** tool. - [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -- [Qt 6.8.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) +- [Qt 6.8.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode. In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs: -- add and set the `QTDIR` environment variable, e.g. `\6.8.1\msvc2022_64\` +- add and set the `QTDIR` environment variable, e.g. `\6.8.2\msvc2022_64\` - or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022) **NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead. In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool): -- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.8.1\msvc2022_64\` +- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.8.2\msvc2022_64\` ### Linux These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager: - Clang 17+ or GCC 13+ - [CMake 3.28.0+](https://www.cmake.org/download/) -- [Qt 6.8.1](https://www.qt.io/download-qt-installer) +- [Qt 6.8.2](https://www.qt.io/download-qt-installer) - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) @@ -119,7 +119,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r ##### Configuring the Qt Plugin (if used) 1) go to `Extensions->Qt VS Tools->Qt Versions` -2) add the path to your Qt installation with compiler e.g. `\6.8.1\msvc2022_64`, version will fill in automatically +2) add the path to your Qt installation with compiler e.g. `\6.8.2\msvc2022_64`, version will fill in automatically 3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**) 4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**) diff --git a/Utilities/BitField.h b/Utilities/BitField.h index 32785e5e7e..b2243dbe1e 100644 --- a/Utilities/BitField.h +++ b/Utilities/BitField.h @@ -256,7 +256,7 @@ struct ff_t : bf_base #endif template -struct fmt_unveil, void> +struct fmt_unveil> { using type = typename fmt_unveil>::type; @@ -267,7 +267,7 @@ struct fmt_unveil, void> }; template -struct fmt_unveil, void> +struct fmt_unveil> { using type = typename fmt_unveil>::type; @@ -278,7 +278,7 @@ struct fmt_unveil, void> }; template -struct fmt_unveil, void> +struct fmt_unveil> { using type = typename fmt_unveil>::type; diff --git a/Utilities/File.h b/Utilities/File.h index f3adaa15d4..828a0384b6 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -1,5 +1,6 @@ #pragma once // No BOM and only basic ASCII in this header, or a neko will die +#include "util/serialization.hpp" #include "util/types.hpp" #include "util/shared_ptr.hpp" #include "bit_set.h" @@ -78,6 +79,8 @@ namespace fs constexpr bool operator==(const stat_t&) const = default; }; + static_assert(utils::Bitcopy); + // Helper, layout is equal to iovec struct struct iovec_clone { @@ -126,6 +129,8 @@ namespace fs using enable_bitcopy = std::false_type; }; + static_assert(!utils::Bitcopy); + // Directory handle base struct dir_base { diff --git a/Utilities/StrFmt.h b/Utilities/StrFmt.h index f83cbe49fe..05d98fdfab 100644 --- a/Utilities/StrFmt.h +++ b/Utilities/StrFmt.h @@ -22,7 +22,7 @@ namespace fmt #endif } -template +template struct fmt_unveil { static_assert(sizeof(T) > 0, "fmt_unveil<> error: incomplete type"); @@ -54,7 +54,8 @@ struct fmt_unveil }; template -struct fmt_unveil && sizeof(T) <= 8 && alignof(T) <= 8>> + requires(std::is_integral_v && sizeof(T) <= 8 && alignof(T) <= 8) +struct fmt_unveil { using type = T; @@ -65,7 +66,8 @@ struct fmt_unveil && sizeof(T) <= 8 && }; template -struct fmt_unveil && sizeof(T) <= 8 && alignof(T) <= 8>> + requires(std::is_floating_point_v && sizeof(T) <= 8 && alignof(T) <= 8) +struct fmt_unveil { using type = T; @@ -77,7 +79,8 @@ struct fmt_unveil && sizeof(T) < }; template -struct fmt_unveil>> + requires std::is_enum_v +struct fmt_unveil { using type = T; @@ -88,7 +91,7 @@ struct fmt_unveil>> }; template -struct fmt_unveil +struct fmt_unveil { using type = std::add_const_t*; @@ -105,7 +108,7 @@ namespace fmt } template -struct fmt_unveil +struct fmt_unveil { using type = std::add_const_t*; @@ -116,7 +119,7 @@ struct fmt_unveil }; template -struct fmt_unveil, void> +struct fmt_unveil> { using type = typename fmt_unveil::type; @@ -127,7 +130,7 @@ struct fmt_unveil, void> }; // String type format provider, also type classifier (format() called if an argument is formatted as "%s") -template +template struct fmt_class_string { // Formatting function (must be explicitly specialized) @@ -200,47 +203,47 @@ struct fmt_class_string }; template <> -struct fmt_class_string +struct fmt_class_string { static void format(std::string& out, u64 arg); }; template -struct fmt_class_string : fmt_class_string +struct fmt_class_string : fmt_class_string { // Classify all pointers as const void* }; template <> -struct fmt_class_string +struct fmt_class_string { static void format(std::string& out, u64 arg); }; template <> -struct fmt_class_string : fmt_class_string +struct fmt_class_string : fmt_class_string { // Classify char* as const char* }; template <> -struct fmt_class_string : fmt_class_string +struct fmt_class_string : fmt_class_string { }; template <> -struct fmt_class_string : fmt_class_string +struct fmt_class_string : fmt_class_string { }; template <> -struct fmt_class_string +struct fmt_class_string { static void format(std::string& out, u64 arg); }; template <> -struct fmt_class_string : fmt_class_string +struct fmt_class_string : fmt_class_string { }; @@ -254,7 +257,7 @@ namespace fmt } template -struct fmt_class_string +struct fmt_class_string { static FORCE_INLINE SAFE_BUFFERS(const T&) get_object(u64 arg) { @@ -275,7 +278,7 @@ namespace fmt } template -struct fmt_class_string +struct fmt_class_string { static FORCE_INLINE SAFE_BUFFERS(const T&) get_object(u64 arg) { diff --git a/Utilities/bit_set.h b/Utilities/bit_set.h index f418fde13e..8b98ae0fd4 100644 --- a/Utilities/bit_set.h +++ b/Utilities/bit_set.h @@ -385,7 +385,7 @@ public: }; template -struct fmt_unveil, void> +struct fmt_unveil> { // Format as is using type = bs_t; diff --git a/Utilities/cheat_info.cpp b/Utilities/cheat_info.cpp index c46f2089e9..a16be2767f 100644 --- a/Utilities/cheat_info.cpp +++ b/Utilities/cheat_info.cpp @@ -4,6 +4,29 @@ LOG_CHANNEL(log_cheat, "Cheat"); +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](cheat_type value) + { + switch (value) + { + case cheat_type::unsigned_8_cheat: return "Unsigned 8 bits"; + case cheat_type::unsigned_16_cheat: return "Unsigned 16 bits"; + case cheat_type::unsigned_32_cheat: return "Unsigned 32 bits"; + case cheat_type::unsigned_64_cheat: return "Unsigned 64 bits"; + case cheat_type::signed_8_cheat: return "Signed 8 bits"; + case cheat_type::signed_16_cheat: return "Signed 16 bits"; + case cheat_type::signed_32_cheat: return "Signed 32 bits"; + case cheat_type::signed_64_cheat: return "Signed 64 bits"; + case cheat_type::float_32_cheat: return "Float 32 bits"; + case cheat_type::max: break; + } + + return unknown; + }); +} + bool cheat_info::from_str(const std::string& cheat_line) { auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false); diff --git a/Utilities/date_time.cpp b/Utilities/date_time.cpp new file mode 100644 index 0000000000..6aef060318 --- /dev/null +++ b/Utilities/date_time.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include "date_time.h" + +#include + +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-%dT%H:%M:%S", dateTime); +} diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 082ffa3ea8..2907949c9f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -33,18 +33,20 @@ jobs: steps: - task: Cache@2 inputs: - key: ccache | $(Agent.OS) | $(COMPILER) + key: ccache | $(Agent.OS) | $(COMPILER) | $(Build.SourceVersion) + restoreKeys: | + ccache | $(Agent.OS) | $(COMPILER) path: $(CCACHE_DIR) displayName: ccache - bash: | - docker pull --quiet rpcs3/rpcs3-ci-jammy:1.0 + docker pull --quiet rpcs3/rpcs3-ci-jammy: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-jammy:1.0 \ + rpcs3/rpcs3-ci-jammy:1.1 \ /rpcs3/.ci/build-linux.sh displayName: Docker setup and build @@ -69,14 +71,19 @@ jobs: variables: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.8.1' + QT_VER: '6.8.2' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202411221531' + QT_DATE: '202501260838' QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64 VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' VULKAN_SDK: C:\VulkanSDK\$(VULKAN_VER) - CACHE_DIR: ./cache + CCACHE_SHA: '6252f081876a9a9f700fae13a5aec5d0d486b28261d7f1f72ac11c7ad9df4da9' + CCACHE_BIN_DIR: 'C:\ccache_bin' + CCACHE_DIR: 'C:\ccache' + CCACHE_INODECACHE: 'true' + CCACHE_SLOPPINESS: 'time_macros' + DEPS_CACHE_DIR: ./dependency_cache UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-win" @@ -89,11 +96,17 @@ jobs: - task: Cache@2 inputs: - key: $(Agent.OS) | $(COMPILER) | "$(QT_VER)" | $(VULKAN_SDK_SHA) | llvm.lock | glslang.lock - path: $(CACHE_DIR) - restoreKeys: | - $(Agent.OS) | $(COMPILER) - displayName: Cache + key: ccache | $(Agent.OS) | $(COMPILER) | "$(Build.SourceVersion)" + path: $(CCACHE_DIR) + restoreKeys: + ccache | $(Agent.OS) | $(COMPILER) + displayName: Build Ccache + + - task: Cache@2 + inputs: + key: $(Agent.OS) | $(COMPILER) | "$(QT_VER)" | $(VULKAN_SDK_SHA) | $(CCACHE_SHA) | llvm.lock | glslang.lock + path: $(DEPS_CACHE_DIR) + displayName: Dependencies Cache - bash: .ci/setup-windows.sh displayName: Download and unpack dependencies @@ -107,6 +120,7 @@ jobs: maximumCpuCount: true platform: x64 configuration: 'Release' + msbuildArgs: /p:CLToolPath=$(CCACHE_BIN_DIR) /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="$(Build.SourcesDirectory)\buildfiles\msvc\ci_no_debug_info.targets" displayName: Compile RPCS3 - bash: .ci/deploy-windows.sh @@ -142,10 +156,10 @@ jobs: steps: - task: Cache@2 inputs: - key: ccache | "$(Agent.OS)" + key: ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(Build.SourceVersion)" path: $(CCACHE_DIR) restoreKeys: | - ccache | "$(Agent.OS)" + ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" displayName: Ccache cache - task: Cache@2 @@ -203,10 +217,10 @@ jobs: steps: - task: Cache@2 inputs: - key: ccache | "$(Agent.OS)" + key: ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(Build.SourceVersion)" path: $(CCACHE_DIR) restoreKeys: | - ccache | "$(Agent.OS)" + ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" displayName: Ccache cache - task: Cache@2 diff --git a/buildfiles/msvc/ci_no_debug_info.targets b/buildfiles/msvc/ci_no_debug_info.targets new file mode 100644 index 0000000000..147806ac97 --- /dev/null +++ b/buildfiles/msvc/ci_no_debug_info.targets @@ -0,0 +1,8 @@ + + + + + None + + + \ No newline at end of file diff --git a/rpcs3/Crypto/decrypt_binaries.cpp b/rpcs3/Crypto/decrypt_binaries.cpp index a87db5c0ec..6205ac6d4f 100644 --- a/rpcs3/Crypto/decrypt_binaries.cpp +++ b/rpcs3/Crypto/decrypt_binaries.cpp @@ -96,7 +96,7 @@ usz decrypt_binaries_t::decrypt(std::string_view klic_input) case "SCE\0"_u32: { // First KLIC is no KLIC - elf_file = decrypt_self(std::move(elf_file), key_it != 0 ? reinterpret_cast(&m_klics[key_it]) : nullptr); + elf_file = decrypt_self(elf_file, key_it != 0 ? reinterpret_cast(&m_klics[key_it]) : nullptr); if (!elf_file) { diff --git a/rpcs3/Crypto/key_vault.cpp b/rpcs3/Crypto/key_vault.cpp index ab99514b27..b9222cd63e 100644 --- a/rpcs3/Crypto/key_vault.cpp +++ b/rpcs3/Crypto/key_vault.cpp @@ -20,6 +20,7 @@ SELF_KEY::SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::str KeyVault::KeyVault() { + std::memcpy(klicensee_key, NP_KLIC_FREE, sizeof(klicensee_key)); } void KeyVault::LoadSelfLV0Keys() @@ -751,15 +752,14 @@ SELF_KEY KeyVault::FindSelfKey(u32 type, u16 revision, u64 version) return key; } -void KeyVault::SetKlicenseeKey(u8* key) +void KeyVault::SetKlicenseeKey(const u8* key) { - klicensee_key = std::make_unique(0x10); - memcpy(klicensee_key.get(), key, 0x10); + std::memcpy(klicensee_key, key, 0x10); } -u8* KeyVault::GetKlicenseeKey() const +const u8* KeyVault::GetKlicenseeKey() const { - return klicensee_key.get(); + return klicensee_key; } void rap_to_rif(unsigned char* rap, unsigned char* rif) diff --git a/rpcs3/Crypto/key_vault.h b/rpcs3/Crypto/key_vault.h index 5fc19d1659..ac5ba084b9 100644 --- a/rpcs3/Crypto/key_vault.h +++ b/rpcs3/Crypto/key_vault.h @@ -319,13 +319,13 @@ class KeyVault std::vector sk_LDR_arr{}; std::vector sk_UNK7_arr{}; std::vector sk_NPDRM_arr{}; - std::unique_ptr klicensee_key{}; + u8 klicensee_key[16]{}; public: KeyVault(); SELF_KEY FindSelfKey(u32 type, u16 revision, u64 version); - void SetKlicenseeKey(u8* key); - u8* GetKlicenseeKey() const; + void SetKlicenseeKey(const u8* key); + const u8* GetKlicenseeKey() const; private: void LoadSelfLV0Keys(); diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index baf612657d..4ca103c78b 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -39,6 +39,13 @@ package_reader::package_reader(const std::string& path) return; } + m_is_valid = set_decryption_key(); + + if (!m_is_valid) + { + return; + } + const bool param_sfo_found = read_param_sfo(); if (!param_sfo_found) @@ -66,7 +73,7 @@ bool package_reader::read_header() } pkg_log.notice("Path: '%s'", m_path); - pkg_log.notice("Header: pkg_magic = 0x%x = \"%s\"", +m_header.pkg_magic, std::string_view(reinterpret_cast(&m_header.pkg_magic + 1), 3)); // Skip 0x7F + pkg_log.notice("Header: pkg_magic = 0x%x = \"%s\"", +m_header.pkg_magic, std::string_view(reinterpret_cast(&m_header.pkg_magic), 4).substr(1)); // Skip 0x7F pkg_log.notice("Header: pkg_type = 0x%x = %d", m_header.pkg_type, m_header.pkg_type); pkg_log.notice("Header: pkg_platform = 0x%x = %d", m_header.pkg_platform, m_header.pkg_platform); pkg_log.notice("Header: meta_offset = 0x%x = %d", m_header.meta_offset, m_header.meta_offset); @@ -93,7 +100,7 @@ bool package_reader::read_header() return false; } - pkg_log.notice("Extended header: magic = 0x%x = \"%s\"", +ext_header.magic, std::string_view(reinterpret_cast(&ext_header.magic + 1), 3)); + pkg_log.notice("Extended header: magic = 0x%x = \"%s\"", +ext_header.magic, std::string_view(reinterpret_cast(&ext_header.magic), 4).substr(1)); pkg_log.notice("Extended header: unknown_1 = 0x%x = %d", ext_header.unknown_1, ext_header.unknown_1); pkg_log.notice("Extended header: ext_hdr_size = 0x%x = %d", ext_header.ext_hdr_size, ext_header.ext_hdr_size); pkg_log.notice("Extended header: ext_data_size = 0x%x = %d", ext_header.ext_data_size, ext_header.ext_data_size); @@ -112,6 +119,12 @@ bool package_reader::read_header() return false; } + if (u64{umax} / sizeof(PKGEntry) < m_header.file_count) + { + pkg_log.error("PKG file count is too large! (0x%x)", m_header.file_count); + return false; + } + switch (const u16 type = m_header.pkg_type) { case PKG_RELEASE_TYPE_DEBUG: break; @@ -187,11 +200,6 @@ bool package_reader::read_header() bool package_reader::read_metadata() { - if (!decrypt_data()) - { - return false; - } - // Read title ID and use it as an installation directory m_install_dir.resize(9); archive_read_block(55, &m_install_dir.front(), m_install_dir.size()); @@ -306,7 +314,7 @@ bool package_reader::read_metadata() if (packet.size == sizeof(m_metadata.qa_digest)) { archive_read(&m_metadata.qa_digest, sizeof(m_metadata.qa_digest)); - pkg_log.notice("Metadata: QA Digest = 0x%x", m_metadata.qa_digest); + pkg_log.notice("Metadata: QA Digest = %s", std::span(m_metadata.qa_digest, sizeof(m_metadata.qa_digest))); continue; } else @@ -478,7 +486,7 @@ bool package_reader::read_metadata() return true; } -bool package_reader::decrypt_data() +bool package_reader::set_decryption_key() { if (!m_is_valid) { @@ -493,12 +501,76 @@ bool package_reader::decrypt_data() aes_context ctx; aes_setkey_enc(&ctx, m_metadata.content_type == 0x15u ? PKG_AES_KEY_VITA_1 : m_metadata.content_type == 0x16u ? PKG_AES_KEY_VITA_2 : PKG_AES_KEY_VITA_3, 128); aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast(&m_header.klicensee), m_dec_key.data()); - decrypt(0, m_header.file_count * sizeof(PKGEntry), m_dec_key.data()); + return true; } - else + + std::memcpy(m_dec_key.data(), PKG_AES_KEY, m_dec_key.size()); + + if (std::vector entries; !read_entries(entries)) { - std::memcpy(m_dec_key.data(), PKG_AES_KEY, m_dec_key.size()); - decrypt(0, m_header.file_count * sizeof(PKGEntry), m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA ? PKG_AES_KEY2 : m_dec_key.data()); + pkg_log.notice("PKG may be IDU, retrying with IDU key."); + + std::memcpy(m_dec_key.data(), PKG_AES_KEY_IDU, m_dec_key.size()); + + if (!read_entries(entries)) + { + pkg_log.error("PKG decryption failed!"); + return false; + } + } + + return true; +} + +bool package_reader::read_entries(std::vector& entries) +{ + entries.clear(); + entries.resize(m_header.file_count + BUF_PADDING / sizeof(PKGEntry) + 1); + + const usz read_size = decrypt(0, m_header.file_count * sizeof(PKGEntry), m_dec_key.data(), entries.data()); + + if (read_size < m_header.file_count * sizeof(PKGEntry)) + { + return false; + } + + entries.resize(m_header.file_count); + + const usz fsz = m_file.size() - m_header.data_offset; + + // Data integrity validation + for (const PKGEntry& entry : entries) + { + if (!entry.name_size) + { + continue; + } + + if (entry.name_size > PKG_MAX_FILENAME_SIZE) + { + return false; + } + + if (fsz < entry.name_size || fsz - entry.name_size < entry.name_offset) + { + // Name exceeds file(s) + return false; + } + + if (entry.file_size) + { + if (fsz < entry.file_size || fsz - entry.file_size < entry.file_offset) + { + // Data exceeds file(s) + return false; + } + + if (entry.name_offset == entry.file_offset) + { + // Repeated value: odd + return false; + } + } } return true; @@ -506,28 +578,34 @@ bool package_reader::decrypt_data() bool package_reader::read_param_sfo() { - if (!decrypt_data()) + std::vector entries; + + if (!read_entries(entries)) { return false; } - std::vector entries(m_header.file_count); - - std::memcpy(entries.data(), m_bufs.back().get(), entries.size() * sizeof(PKGEntry)); + std::vector data_buf; for (const PKGEntry& entry : entries) { - if (entry.name_size > 256) + if (entry.name_size > PKG_MAX_FILENAME_SIZE) { - pkg_log.error("PKG name size is too big (0x%x)", entry.name_size); + pkg_log.error("PKG name size is too big (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset); continue; } const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u; - decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data()); + std::string name(entry.name_size + BUF_PADDING, '\0'); - const std::string_view name{reinterpret_cast(m_bufs.back().get()), entry.name_size}; + if (usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name.data()); read_size < entry.name_size) + { + pkg_log.error("PKG name could not be read (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset); + continue; + } + + fmt::trim_back(name, "\0"sv); // We're looking for the PARAM.SFO file, if there is any if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO") @@ -536,19 +614,21 @@ bool package_reader::read_param_sfo() } // Read the package's PARAM.SFO - if (fs::file tmp = fs::make_stream>()) + fs::file tmp = fs::make_stream>(); { for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE) { const u64 block_size = std::min(BUF_SIZE, entry.file_size - pos); - if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data()).size() != block_size) + data_buf.resize(block_size + BUF_PADDING); + + if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), data_buf.data()) != block_size) { pkg_log.error("Failed to decrypt PARAM.SFO file"); return false; } - if (tmp.write(m_bufs.back().get(), block_size) != block_size) + if (tmp.write(data_buf.data(), block_size) != block_size) { pkg_log.error("Failed to write to temporary PARAM.SFO file"); return false; @@ -567,9 +647,6 @@ bool package_reader::read_param_sfo() return true; } - - pkg_log.error("Failed to create temporary PARAM.SFO file"); - return false; } return false; @@ -757,31 +834,38 @@ bool package_reader::fill_data(std::map& all_instal m_entry_indexer = 0; m_written_bytes = 0; - if (!decrypt_data()) + usz num_failures = 0; + + std::vector entries; + + if (!read_entries(entries)) { return false; } - usz num_failures = 0; - - std::vector entries(m_header.file_count); - - std::memcpy(entries.data(), m_bufs.back().get(), entries.size() * sizeof(PKGEntry)); - // Create directories first for (const auto& entry : entries) { if (entry.name_size > PKG_MAX_FILENAME_SIZE) { num_failures++; - pkg_log.error("PKG name size is too big (0x%x)", entry.name_size); + pkg_log.error("PKG name size is too big (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset); break; } - const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u; - decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data()); + std::string name(entry.name_size + BUF_PADDING, '\0'); + + const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u; + + if (const usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name.data()); read_size < entry.name_size) + { + num_failures++; + pkg_log.error("PKG name could not be read (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset); + break; + } + + fmt::trim_back(name, "\0"sv); - const std::string_view name{reinterpret_cast(m_bufs.back().get()), entry.name_size}; std::string path = m_install_path + vfs::escape(name); if (entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS)) @@ -863,7 +947,7 @@ bool package_reader::fill_data(std::map& all_instal fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic); -void package_reader::extract_worker(thread_key thread_data_key) +void package_reader::extract_worker() { std::vector read_cache; @@ -1059,23 +1143,21 @@ void package_reader::extract_worker(thread_key thread_data_key) { const u64 block_size = std::min({BUF_SIZE, std::max(size * 5 / 3, 65536), entry.file_size - pos}); - read_cache.resize(block_size); + read_cache.resize(block_size + BUF_PADDING); cache_off = pos; - const std::span data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key); + const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), read_cache.data()); - if (data_span.empty()) + if (!advance_size) { cache_off = umax; - read_cache.clear(); return 0; } - read_cache.resize(data_span.size()); - std::memcpy(read_cache.data(), data_span.data(), data_span.size()); + read_cache.resize(advance_size); - size = std::min(data_span.size(), size); - std::memcpy(ptr, data_span.data(), size); + size = std::min(advance_size, size); + std::memcpy(ptr, read_cache.data(), size); return size; } @@ -1083,17 +1165,15 @@ void package_reader::extract_worker(thread_key thread_data_key) { const u64 block_size = std::min(BUF_SIZE, size - read_size); - const std::span data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key); + const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), static_cast(ptr) + read_size); - if (data_span.empty()) + if (!advance_size) { break; } - std::memcpy(static_cast(ptr) + read_size, data_span.data(), data_span.size()); - - read_size += data_span.size(); - pos += data_span.size(); + read_size += advance_size; + pos += advance_size; } return read_size + size_cache_end; @@ -1121,9 +1201,9 @@ void package_reader::extract_worker(thread_key thread_data_key) } // 16MB buffer - std::vector buffer(std::min(entry.file_size, 1u << 24)); + std::vector buffer(std::min(entry.file_size, 1u << 24) + BUF_PADDING); - while (usz read_size = final_data.read(buffer.data(), buffer.size())) + while (usz read_size = final_data.read(buffer.data(), buffer.size() - BUF_PADDING)) { out.write(buffer.data(), read_size); m_written_bytes += read_size; @@ -1224,20 +1304,15 @@ package_install_result package_reader::extract_data(std::deque& if (reader.m_num_failures == 0) { - reader.m_bufs.resize(std::min(utils::get_thread_count(), reader.m_install_entries.size())); + const usz thread_count = std::min(utils::get_thread_count(), reader.m_install_entries.size()); - atomic_t thread_indexer = 0; - - named_thread_group workers("PKG Installer "sv, std::max(::narrow(reader.m_bufs.size()), 1) - 1, [&]() + named_thread_group workers("PKG Installer "sv, std::max(::narrow(thread_count), 1) - 1, [&]() { - reader.extract_worker(thread_key{thread_indexer++}); + reader.extract_worker(); }); - reader.extract_worker(thread_key{thread_indexer++}); + reader.extract_worker(); workers.join(); - - reader.m_bufs.clear(); - reader.m_bufs.shrink_to_fit(); } num_failures += reader.m_num_failures; @@ -1312,33 +1387,25 @@ std::span package_reader::archive_read_block(u64 offset, void* data_ return {static_cast(data_ptr), read_n}; } -std::span package_reader::decrypt(u64 offset, u64 size, const uchar* key, thread_key thread_data_key) +usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_buf) { if (!m_is_valid) { - return {}; + return 0; } - if (m_bufs.empty()) + if (m_header.data_offset > ~offset) { - // Assume in single-threaded mode still - m_bufs.resize(1); - } - - auto& local_buf = ::at32(m_bufs, thread_data_key.unique_num); - - if (!local_buf) - { - // Allocate buffer with BUF_SIZE size or more if required - local_buf.reset(new u128[std::max(BUF_SIZE, sizeof(PKGEntry) * m_header.file_count) / sizeof(u128)]); + return 0; } // Read the data and set available size - const auto data_span = archive_read_block(m_header.data_offset + offset, local_buf.get(), size); - ensure(data_span.data() == static_cast(local_buf.get())); + const auto data_span = archive_read_block(m_header.data_offset + offset, local_buf, size); + ensure(data_span.data() == static_cast(local_buf)); // Get block count const u64 blocks = (data_span.size() + 15) / 16; + const auto out_data = reinterpret_cast(local_buf); if (m_header.pkg_type == PKG_RELEASE_TYPE_DEBUG) { @@ -1356,15 +1423,15 @@ std::span package_reader::decrypt(u64 offset, u64 size, const uchar* // Initialize stream cipher for current position input[7] = offset / 16 + i; - union sha1_hash + struct sha1_hash { u8 data[20]; - u128 _v128; - } hash; + } hash{}; sha1(reinterpret_cast(input), sizeof(input), hash.data); - local_buf[i] ^= hash._v128; + const u128 v = read_from_ptr(out_data, i * 16); + write_to_ptr(out_data, i * 16, v ^ read_from_ptr(hash.data)); } } else if (m_header.pkg_type == PKG_RELEASE_TYPE_RELEASE) @@ -1384,7 +1451,8 @@ std::span package_reader::decrypt(u64 offset, u64 size, const uchar* aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast(&input), reinterpret_cast(&key)); - local_buf[i] ^= key; + const u128 v = read_from_ptr(out_data, i * 16); + write_to_ptr(out_data, i * 16, v ^ key); } } else @@ -1392,8 +1460,14 @@ std::span package_reader::decrypt(u64 offset, u64 size, const uchar* pkg_log.error("Unknown release type (0x%x)", m_header.pkg_type); } + if (blocks * 16 != size) + { + // Put NTS and other zeroes on unaligned reads + std::memset(out_data + size, 0, blocks * 16 - size); + } + // Return the amount of data written in buf - return data_span; + return std::min(size, data_span.size()); } int package_reader::get_progress(int maximum) const diff --git a/rpcs3/Crypto/unpkg.h b/rpcs3/Crypto/unpkg.h index 0f5c8faba1..8a7c482f16 100644 --- a/rpcs3/Crypto/unpkg.h +++ b/rpcs3/Crypto/unpkg.h @@ -314,11 +314,6 @@ struct package_install_result class package_reader { - struct thread_key - { - const usz unique_num = umax; - }; - struct install_entry { typename std::map::value_type* weak_reference{}; @@ -354,7 +349,7 @@ public: bool is_valid() const { return m_is_valid; } package_install_result check_target_app_version() const; static package_install_result extract_data(std::deque& readers, std::deque& bootable_paths); - psf::registry get_psf() const { return m_psf; } + const psf::registry& get_psf() const { return m_psf; } result get_result() const { return m_result; }; int get_progress(int maximum = 100) const; @@ -365,14 +360,15 @@ private: bool read_header(); bool read_metadata(); bool read_param_sfo(); - bool decrypt_data(); + bool set_decryption_key(); + bool read_entries(std::vector& entries); void archive_seek(s64 new_offset, const fs::seek_mode damode = fs::seek_set); u64 archive_read(void* data_ptr, u64 num_bytes); bool set_install_path(); bool fill_data(std::map& all_install_entries); std::span archive_read_block(u64 offset, void* data_ptr, u64 num_bytes); - std::span decrypt(u64 offset, u64 size, const uchar* key, thread_key thread_data_key = {0}); - void extract_worker(thread_key thread_data_key); + usz decrypt(u64 offset, u64 size, const uchar* key, void* local_buf); + void extract_worker(); std::deque m_install_entries; std::string m_install_path; @@ -383,6 +379,7 @@ private: bool m_was_null = false; static constexpr usz BUF_SIZE = 8192 * 1024; // 8 MB + static constexpr usz BUF_PADDING = 32; bool m_is_valid = false; result m_result = result::not_started; @@ -390,7 +387,6 @@ private: std::string m_path{}; std::string m_install_dir{}; fs::file m_file{}; - std::vector> m_bufs{}; std::array m_dec_key{}; PKGHeader m_header{}; diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index 66000c4f12..3855dcd62f 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -1045,11 +1045,8 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size) } else if (npd->license == 3) // Free license. { - // Use klicensee if available. - if (key_v.GetKlicenseeKey()) - memcpy(npdrm_key, key_v.GetKlicenseeKey(), 0x10); - else - memcpy(npdrm_key, NP_KLIC_FREE, 0x10); + // Use klicensee if available. (may be set to NP_KLIC_FREE if none is set) + std::memcpy(npdrm_key, key_v.GetKlicenseeKey(), 0x10); } else { @@ -1085,7 +1082,7 @@ const NPD_HEADER* SELFDecrypter::GetNPDHeader() const return nullptr; } -bool SELFDecrypter::LoadMetadata(u8* klic_key) +bool SELFDecrypter::LoadMetadata(const u8* klic_key) { aes_context aes; const auto metadata_info = std::make_unique(sizeof(meta_info)); @@ -1319,11 +1316,11 @@ static bool IsDebugSelf(const fs::file& f) return false; } -static bool CheckDebugSelf(fs::file& s) +static fs::file CheckDebugSelf(const fs::file& s) { if (s.size() < 0x18) { - return false; + return {}; } // Get the key version. @@ -1352,15 +1349,14 @@ static bool CheckDebugSelf(fs::file& s) e.write(buf, size); } - s = std::move(e); - return true; + return e; } // Leave the file untouched. - return false; + return {}; } -fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* out_info, bool require_encrypted) +fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key, SelfAdditionalInfo* out_info) { if (out_info) { @@ -1377,10 +1373,10 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou // Check SELF header first. Check for a debug SELF. if (elf_or_self.size() >= 4 && elf_or_self.read() == "SCE\0"_u32) { - if (CheckDebugSelf(elf_or_self)) + if (fs::file res = CheckDebugSelf(elf_or_self)) { // TODO: Decrypt - return elf_or_self; + return res; } // Check the ELF file class (32 or 64 bit). @@ -1399,14 +1395,14 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou // Load and decrypt the SELF file metadata. if (!self_dec.LoadMetadata(klic_key)) { - self_log.error("Failed to load SELF file metadata!"); + (klic_key ? self_log.notice : self_log.error)("Failed to load SELF file metadata!"); return fs::file{}; } // Decrypt the SELF file data. if (!self_dec.DecryptData()) { - self_log.error("Failed to decrypt SELF file data!"); + (klic_key ? self_log.notice : self_log.error)("Failed to decrypt SELF file data!"); return fs::file{}; } @@ -1414,12 +1410,7 @@ 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; + return {}; } bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key, NPD_HEADER* npd_out) diff --git a/rpcs3/Crypto/unself.h b/rpcs3/Crypto/unself.h index afa3416095..1279bc961d 100644 --- a/rpcs3/Crypto/unself.h +++ b/rpcs3/Crypto/unself.h @@ -476,7 +476,7 @@ public: fs::file MakeElf(bool isElf32); bool LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info = nullptr); void ShowHeaders(bool isElf32); - bool LoadMetadata(u8* klic_key); + bool LoadMetadata(const u8* klic_key); bool DecryptData(); bool DecryptNPDRM(u8 *metadata, u32 metadata_size); const NPD_HEADER* GetNPDHeader() const; @@ -559,7 +559,7 @@ private: } }; -fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr, bool require_encrypted = false); +fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr); 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/Emu/Audio/XAudio2/XAudio2Backend.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index 3931638c69..0a84d747bf 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -65,7 +65,7 @@ XAudio2Backend::XAudio2Backend() m_com_init_success = true; } - if (HRESULT hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_USE_DEFAULT_PROCESSOR); FAILED(hr)) + if (HRESULT hr = XAudio2Create(&instance, 0, XAUDIO2_USE_DEFAULT_PROCESSOR); FAILED(hr)) { XAudio.error("XAudio2Create() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); return; @@ -78,7 +78,7 @@ XAudio2Backend::XAudio2Backend() } // Try to register a listener for device changes - if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.GetAddressOf())); FAILED(hr)) + if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator)); FAILED(hr)) { XAudio.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); return; @@ -215,7 +215,7 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi if (use_default_device) { Microsoft::WRL::ComPtr default_dev{}; - if (HRESULT hr = m_device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, default_dev.GetAddressOf()); FAILED(hr)) + if (HRESULT hr = m_device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &default_dev); FAILED(hr)) { XAudio.error("GetDefaultAudioEndpoint() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); return false; @@ -319,7 +319,7 @@ f64 XAudio2Backend::GetCallbackFrameLen() Microsoft::WRL::ComPtr xaudio_ext{}; f64 min_latency{}; - if (HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, std::bit_cast(xaudio_ext.GetAddressOf())); FAILED(hr)) + if (HRESULT hr = m_xaudio2_instance.As(&xaudio_ext); FAILED(hr)) { XAudio.error("QueryInterface() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); } diff --git a/rpcs3/Emu/Audio/XAudio2/xaudio2_enumerator.cpp b/rpcs3/Emu/Audio/XAudio2/xaudio2_enumerator.cpp index 5f1fd1f44c..0655f29dca 100644 --- a/rpcs3/Emu/Audio/XAudio2/xaudio2_enumerator.cpp +++ b/rpcs3/Emu/Audio/XAudio2/xaudio2_enumerator.cpp @@ -26,7 +26,7 @@ xaudio2_enumerator::~xaudio2_enumerator() std::vector xaudio2_enumerator::get_output_devices() { Microsoft::WRL::ComPtr devEnum{}; - if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(devEnum.GetAddressOf())); FAILED(hr)) + if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&devEnum)); FAILED(hr)) { xaudio_dev_enum.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); return {}; @@ -57,7 +57,7 @@ std::vector xaudio2_enumerator::get_outpu for (UINT dev_idx = 0; dev_idx < count; dev_idx++) { Microsoft::WRL::ComPtr endpoint{}; - if (HRESULT hr = devices->Item(dev_idx, endpoint.GetAddressOf()); FAILED(hr)) + if (HRESULT hr = devices->Item(dev_idx, &endpoint); FAILED(hr)) { xaudio_dev_enum.error("devices->Item() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); continue; @@ -83,7 +83,7 @@ std::vector xaudio2_enumerator::get_outpu CoTaskMemFree(id); Microsoft::WRL::ComPtr props{}; - if (HRESULT hr = endpoint->OpenPropertyStore(STGM_READ, props.GetAddressOf()); FAILED(hr)) + if (HRESULT hr = endpoint->OpenPropertyStore(STGM_READ, &props); FAILED(hr)) { xaudio_dev_enum.error("endpoint->OpenPropertyStore() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); continue; diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index cd4cdb2a46..562dd29476 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -48,6 +48,7 @@ target_include_directories(rpcs3_emu target_sources(rpcs3_emu PRIVATE ../util/atomic.cpp ../util/console.cpp + ../util/emu_utils.cpp ../util/media_utils.cpp ../util/video_provider.cpp ../util/logs.cpp @@ -61,6 +62,7 @@ target_sources(rpcs3_emu PRIVATE ../../Utilities/cheat_info.cpp ../../Utilities/cond.cpp ../../Utilities/Config.cpp + ../../Utilities/date_time.cpp ../../Utilities/File.cpp ../../Utilities/JITASM.cpp ../../Utilities/JITLLVM.cpp @@ -482,6 +484,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Common/surface_store.cpp RSX/Common/TextureUtils.cpp RSX/Common/texture_cache.cpp + RSX/Common/texture_cache_types.cpp RSX/Core/RSXContext.cpp RSX/Core/RSXDisplay.cpp RSX/Core/RSXDrawCommands.cpp @@ -571,6 +574,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Program/program_util.cpp RSX/Program/SPIRVCommon.cpp RSX/Program/VertexProgramDecompiler.cpp + RSX/GSFrameBase.cpp RSX/RSXDisAsm.cpp RSX/RSXFIFO.cpp RSX/RSXOffload.cpp @@ -593,8 +597,10 @@ if(TARGET 3rdparty_vulkan) RSX/VK/vkutils/descriptors.cpp RSX/VK/vkutils/image.cpp RSX/VK/vkutils/image_helpers.cpp + RSX/VK/vkutils/instance.cpp RSX/VK/vkutils/scratch.cpp RSX/VK/vkutils/sync.cpp + RSX/VK/vkutils/swapchain.cpp RSX/VK/vkutils/memory.cpp RSX/VK/vkutils/device.cpp RSX/VK/vkutils/sampler.cpp @@ -627,11 +633,6 @@ if(TARGET 3rdparty_vulkan) RSX/VK/VKVertexProgram.cpp RSX/VK/VKTextureCache.cpp ) - if(MSVC) - set_source_files_properties(RSX/VK/vkutils/shared.cpp PROPERTIES - COMPILE_FLAGS /EHs- - SKIP_PRECOMPILE_HEADERS ON) - endif() endif() target_link_libraries(rpcs3_emu diff --git a/rpcs3/Emu/CPU/CPUDisAsm.h b/rpcs3/Emu/CPU/CPUDisAsm.h index 1b4fc5515b..9e9054d62b 100644 --- a/rpcs3/Emu/CPU/CPUDisAsm.h +++ b/rpcs3/Emu/CPU/CPUDisAsm.h @@ -110,7 +110,8 @@ protected: virtual u32 DisAsmBranchTarget(s32 /*imm*/); // TODO: Add builtin fmt helpper for best performance - template , int> = 0> + template + requires std::is_integral_v static std::string SignedHex(T value) { const auto v = static_cast>(value); diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 520446e991..242dcb9d9f 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -1302,7 +1302,7 @@ cpu_thread* cpu_thread::get_next_cpu() return nullptr; } -std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle); +extern std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle); void cpu_thread::dump_all(std::string& ret) const { diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 7bab6b335a..088bd0801b 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -2,6 +2,12 @@ #ifdef LLVM_AVAILABLE +#include "util/types.hpp" +#include "util/sysinfo.hpp" +#include "Utilities/StrFmt.h" +#include "Utilities/JIT.h" +#include "util/v128.hpp" + #ifdef _MSC_VER #pragma warning(push, 0) #else @@ -24,7 +30,9 @@ #include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/IntrinsicsX86.h" +#ifdef ARCH_ARM64 #include "llvm/IR/IntrinsicsAArch64.h" +#endif #include "llvm/IR/InlineAsm.h" #ifdef _MSC_VER @@ -33,12 +41,6 @@ #pragma GCC diagnostic pop #endif -#include "util/types.hpp" -#include "util/sysinfo.hpp" -#include "Utilities/StrFmt.h" -#include "Utilities/JIT.h" - -#include "util/v128.hpp" #include #include @@ -60,9 +62,8 @@ template concept LLVMValue = (std::is_pointer_v) && (std::is_base_of_v>); template -concept DSLValue = requires (T& v) -{ - { v.eval(std::declval*>()) } -> LLVMValue; +concept DSLValue = requires(T& v, llvm::IRBuilder<>* ir) { + { v.eval(ir) } -> LLVMValue; }; template @@ -476,31 +477,33 @@ struct llvm_value_t : llvm_value_t > template using llvm_expr_t = std::decay_t; -template +template struct is_llvm_expr { }; -template -struct is_llvm_expr().eval(std::declval*>()))>> +template +struct is_llvm_expr { using type = typename std::decay_t::type; }; -template +template struct is_llvm_expr_of { static constexpr bool ok = false; }; template -struct is_llvm_expr_of::type, typename is_llvm_expr::type>> + requires(requires { typename is_llvm_expr::type; } && requires { typename is_llvm_expr::type; }) +struct is_llvm_expr_of { static constexpr bool ok = std::is_same_v::type, typename is_llvm_expr::type>; }; template -using llvm_common_t = std::enable_if_t<(is_llvm_expr_of::ok && ...), typename is_llvm_expr::type>; + requires(is_llvm_expr_of::ok && ...) +using llvm_common_t = typename is_llvm_expr::type; template using llvm_match_tuple = decltype(std::tuple_cat(std::declval&>().match(std::declval(), nullptr)...)); @@ -1606,7 +1609,8 @@ struct llvm_ord }; template -llvm_ord(T&&) -> llvm_ord>::value, T&&>>; + requires is_llvm_cmp>::value +llvm_ord(T&&) -> llvm_ord; template > struct llvm_uno @@ -1659,7 +1663,8 @@ struct llvm_uno }; template -llvm_uno(T&&) -> llvm_uno>::value, T&&>>; + requires is_llvm_cmp>::value +llvm_uno(T&&) -> llvm_uno; template inline llvm_cmp operator ==(T1&& a1, T2&& a2) @@ -3020,7 +3025,7 @@ struct llvm_calli if (((std::get(r) = std::get(a).match(v[I], _m), v[I]) && ...)) { return std::tuple_cat(std::get(r)...); - } + } } } } @@ -3194,14 +3199,16 @@ public: return {}; } - template > + template + requires requires { typename llvm_common_t; } static auto match_expr(llvm::Value* v, llvm::Module* _m, T&& expr) { auto r = expr.match(v, _m); return std::tuple_cat(std::make_tuple(v != nullptr), r); } - template > + template + requires requires { typename llvm_common_t; } auto match_expr(T&& arg, U&& expr) -> decltype(std::tuple_cat(std::make_tuple(false), expr.match(std::declval(), nullptr))) { auto v = arg.eval(m_ir); @@ -3236,202 +3243,235 @@ public: return expr_t{std::forward(expr), std::move(matcher)}; } - template >::value>> + template + requires is_llvm_cmp>::value static auto fcmp_ord(T&& cmp_expr) { return llvm_ord{std::forward(cmp_expr)}; } - template >::value>> + template + requires is_llvm_cmp>::value static auto fcmp_uno(T&& cmp_expr) { return llvm_uno{std::forward(cmp_expr)}; } - template ::is_ok>> + template + requires llvm_noncast::is_ok static auto noncast(T&& expr) { return llvm_noncast{std::forward(expr)}; } - template ::is_ok>> + template + requires llvm_bitcast::is_ok static auto bitcast(T&& expr) { return llvm_bitcast{std::forward(expr)}; } - template ::is_ok>> + template + requires llvm_fpcast::is_ok static auto fpcast(T&& expr) { return llvm_fpcast{std::forward(expr)}; } - template ::is_ok>> + template + requires llvm_trunc::is_ok static auto trunc(T&& expr) { return llvm_trunc{std::forward(expr)}; } - template ::is_ok>> + template + requires llvm_sext::is_ok static auto sext(T&& expr) { return llvm_sext{std::forward(expr)}; } - template ::is_ok>> + template + requires llvm_zext::is_ok static auto zext(T&& expr) { return llvm_zext{std::forward(expr)}; } - template ::is_ok>> + template + requires llvm_select::is_ok static auto select(T&& c, U&& a, V&& b) { return llvm_select{std::forward(c), std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_min::is_ok static auto min(T&& a, U&& b) { return llvm_min{std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_min::is_ok static auto max(T&& a, U&& b) { return llvm_max{std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_fshl::is_ok static auto fshl(T&& a, U&& b, V&& c) { return llvm_fshl{std::forward(a), std::forward(b), std::forward(c)}; } - template ::is_ok>> + template + requires llvm_fshr::is_ok static auto fshr(T&& a, U&& b, V&& c) { return llvm_fshr{std::forward(a), std::forward(b), std::forward(c)}; } - template ::is_ok>> + template + requires llvm_rol::is_ok static auto rol(T&& a, U&& b) { return llvm_rol{std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_add_sat::is_ok static auto add_sat(T&& a, U&& b) { return llvm_add_sat{std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_sub_sat::is_ok static auto sub_sat(T&& a, U&& b) { return llvm_sub_sat{std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_extract::is_ok static auto extract(T&& v, U&& i) { return llvm_extract{std::forward(v), std::forward(i)}; } - template >::is_ok>> + template + requires llvm_extract>::is_ok static auto extract(T&& v, u32 i) { return llvm_extract>{std::forward(v), llvm_const_int{i}}; } - template ::is_ok>> + template + requires llvm_insert::is_ok static auto insert(T&& v, U&& i, V&& e) { return llvm_insert{std::forward(v), std::forward(i), std::forward(e)}; } - template , V>::is_ok>> + template + requires llvm_insert, V>::is_ok static auto insert(T&& v, u32 i, V&& e) { return llvm_insert, V>{std::forward(v), llvm_const_int{i}, std::forward(e)}; } - template ::is_ok>> + template + requires llvm_const_int::is_ok static auto splat(u64 c) { return llvm_const_int{c}; } - template ::is_ok>> + template + requires llvm_const_float::is_ok static auto fsplat(f64 c) { return llvm_const_float{c}; } - template ::is_ok>> + template + requires llvm_splat::is_ok static auto vsplat(U&& v) { return llvm_splat{std::forward(v)}; } - template ::is_ok>> + template + requires llvm_const_vector::is_ok static auto build(Args... args) { return llvm_const_vector{static_cast>(args)...}; } - template ::is_ok>> + template + requires llvm_zshuffle::is_ok static auto zshuffle(T&& v, Args... indices) { return llvm_zshuffle{std::forward(v), {static_cast(indices)...}}; } - template ::is_ok>> + template + requires llvm_shuffle2::is_ok static auto shuffle2(T&& v1, U&& v2, Args... indices) { return llvm_shuffle2{std::forward(v1), std::forward(v2), {static_cast(indices)...}}; } - template ::is_ok>> + template + requires llvm_ctlz::is_ok static auto ctlz(T&& a) { return llvm_ctlz{std::forward(a)}; } - template ::is_ok>> + template + requires llvm_ctpop::is_ok static auto ctpop(T&& a) { return llvm_ctpop{std::forward(a)}; } // Average: (a + b + 1) >> 1 - template ::is_ok>> + template + requires llvm_avg::is_ok static auto avg(T&& a, U&& b) { return llvm_avg{std::forward(a), std::forward(b)}; } - template ::is_ok>> + template + requires llvm_fsqrt::is_ok static auto fsqrt(T&& a) { return llvm_fsqrt{std::forward(a)}; } - template ::is_ok>> + template + requires llvm_fabs::is_ok static auto fabs(T&& a) { return llvm_fabs{std::forward(a)}; } // Optionally opportunistic hardware FMA, can be used if results are identical for all possible input values - template ::is_ok>> + template + requires llvm_fmuladd::is_ok static auto fmuladd(T&& a, U&& b, V&& c, bool strict_fma) { return llvm_fmuladd{std::forward(a), std::forward(b), std::forward(c), strict_fma}; } // Opportunistic hardware FMA, can be used if results are identical for all possible input values - template ::is_ok>> + template + requires llvm_fmuladd::is_ok auto fmuladd(T&& a, U&& b, V&& c) { return llvm_fmuladd{std::forward(a), std::forward(b), std::forward(c), m_use_fma}; @@ -3754,7 +3794,8 @@ public: return load_const(g, i, get_type()); } - template requires requires () { std::declval().eval(std::declval*>()); } + template + requires requires(I& i, llvm::IRBuilder<>* ir) { i.eval(ir); } value_t load_const(llvm::GlobalVariable* g, I i) { value_t result; @@ -3873,7 +3914,8 @@ public: return llvm_calli{"any_select_by_bit4", {std::forward(m), std::forward(a), std::forward(b)}}; } - template , f32[4]>>> + template + requires std::is_same_v, f32[4]> static auto fre(T&& a) { #if defined(ARCH_X64) @@ -3883,7 +3925,8 @@ public: #endif } - template , f32[4]>>> + template + requires std::is_same_v, f32[4]> static auto frsqe(T&& a) { #if defined(ARCH_X64) @@ -3893,7 +3936,8 @@ public: #endif } - template , f32[4]>>> + template + requires std::is_same_v, f32[4]> static auto fmax(T&& a, U&& b) { #if defined(ARCH_X64) @@ -3903,7 +3947,8 @@ public: #endif } - template , f32[4]>>> + template + requires std::is_same_v, f32[4]> static auto fmin(T&& a, U&& b) { #if defined(ARCH_X64) @@ -3913,13 +3958,15 @@ public: #endif } - template , u8[16]>>> + template + requires std::is_same_v, u8[16]> static auto vdbpsadbw(T&& a, U&& b, u8 c) { return llvm_calli>{"llvm.x86.avx512.dbpsadbw.128", {std::forward(a), std::forward(b), llvm_const_int{c}}}; } - template , f32[4]>>> + template + requires std::is_same_v, f32[4]> static auto vrangeps(T&& a, U&& b, u8 c, u8 d) { return llvm_calli, T, llvm_const_int>{"llvm.x86.avx512.mask.range.ps.128", {std::forward(a), std::forward(b), llvm_const_int{c}, std::forward(a), llvm_const_int{d}}}; @@ -3928,7 +3975,7 @@ public: // Format llvm::SizeType template <> -struct fmt_unveil +struct fmt_unveil { using type = usz; diff --git a/rpcs3/Emu/Cell/ErrorCodes.h b/rpcs3/Emu/Cell/ErrorCodes.h index 7b3b0dc1bb..7f6a3892da 100644 --- a/rpcs3/Emu/Cell/ErrorCodes.h +++ b/rpcs3/Emu/Cell/ErrorCodes.h @@ -82,11 +82,11 @@ constexpr FORCE_INLINE CellNotAnError not_an_error(const T& value) return static_cast(static_cast(value)); } -template +template struct ppu_gpr_cast_impl; template <> -struct ppu_gpr_cast_impl +struct ppu_gpr_cast_impl { static inline u64 to(const error_code& code) { diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 98e4692927..0cad02959d 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -590,8 +590,8 @@ f32 microphone_device::calculate_energy_level() sum_squares += normalized_sample * normalized_sample; } - const f32 rms = std::sqrt(sum_squares / num_samples); - const f32 decibels_max = 90.0f; + const f32 rms = num_samples > 0 ? std::sqrt(sum_squares / num_samples) : 0.0f; + constexpr f32 decibels_max = 90.0f; const f32 decibels_relative = 20.0f * std::log10(std::max(rms, 0.00001f)); const f32 decibels = decibels_max + (decibels_relative * 0.5f); diff --git a/rpcs3/Emu/Cell/Modules/cellSail.h b/rpcs3/Emu/Cell/Modules/cellSail.h index 2b34e015b4..91e66dc5f0 100644 --- a/rpcs3/Emu/Cell/Modules/cellSail.h +++ b/rpcs3/Emu/Cell/Modules/cellSail.h @@ -1,5 +1,6 @@ #pragma once +#include "Emu/Memory/vm_ptr.h" #include "cellVpost.h" // Error Codes @@ -672,11 +673,11 @@ union CellSailEvent be_t value; }; -template +template struct ppu_gpr_cast_impl; -template<> -struct ppu_gpr_cast_impl +template <> +struct ppu_gpr_cast_impl { static inline u64 to(const CellSailEvent& event) { diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.h b/rpcs3/Emu/Cell/Modules/sceNpTrophy.h index dfea517fe0..941727648d 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.h +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.h @@ -3,6 +3,7 @@ #include "util/types.hpp" #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/ErrorCodes.h" +#include #include #include diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index a936eb6853..808c31f8a5 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -12,7 +12,7 @@ LOG_CHANNEL(ppu_validator); -const ppu_decoder s_ppu_itype; +extern const ppu_decoder g_ppu_itype; template<> void fmt_class_string::format(std::string& out, u64 arg) @@ -535,6 +535,7 @@ static constexpr struct const_tag{} is_const; static constexpr struct range_tag{} is_range; static constexpr struct min_value_tag{} minv; static constexpr struct max_value_tag{} maxv; +static constexpr struct sign_bit_tag{} sign_bitv; static constexpr struct load_addr_tag{} load_addrv; struct reg_state_t @@ -548,13 +549,13 @@ struct reg_state_t // Check if state is a constant value bool operator()(const_tag) const { - return value_range == 1 && bit_range == 0; + return !is_loaded && value_range == 1 && bit_range == 0; } // Check if state is a ranged value bool operator()(range_tag) const { - return bit_range == 0; + return !is_loaded && bit_range == 0; } // Get minimum bound @@ -569,6 +570,11 @@ struct reg_state_t return value_range ? (ge_than | bit_range) + value_range : 0; } + u64 operator()(sign_bit_tag) const + { + return value_range == 0 || (bit_range >> 63) || (ge_than + value_range - 1) >> 63 != (ge_than >> 63) ? u64{umax} : (ge_than >> 63); + } + u64 operator()(load_addr_tag) const { return is_loaded ? ge_than : 0; @@ -922,7 +928,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const ppu_opcode_t op{+range[index]}; - switch (s_ppu_itype.decode(op.opcode)) + switch (g_ppu_itype.decode(op.opcode)) { case ppu_itype::UNK: { @@ -962,7 +968,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con // Register new function auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function_ext& { - if (addr < start || addr >= end || s_ppu_itype.decode(*get_ptr(addr)) == ppu_itype::UNK) + if (addr < start || addr >= end || g_ppu_itype.decode(*get_ptr(addr)) == ppu_itype::UNK) { if (!fmap.contains(addr)) { @@ -1337,7 +1343,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const u32 iaddr = _ptr.addr(); const ppu_opcode_t op{*ptr}; - const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + const ppu_itype::type type = g_ppu_itype.decode(op.opcode); if ((type == ppu_itype::B || type == ppu_itype::BC) && op.lk && (!op.aa || verify_ref(iaddr))) { @@ -1365,25 +1371,68 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con u32 addr = 0; u32 size = 0; u32 parent_block_idx = umax; - u64 mapped_registers_mask = 0; - u64 moved_registers_mask = 0; + ppua_reg_mask_t mapped_registers_mask{0}; + ppua_reg_mask_t moved_registers_mask{0}; }; // Block analysis workload std::vector block_queue_storage; + bool is_function_caller_analysis = false; + // Main loop (func_queue may grow) - for (usz i = 0; i < func_queue.size(); i++) + for (usz i = 0; i <= func_queue.size(); i++) { + if (i == func_queue.size()) + { + if (is_function_caller_analysis) + { + break; + } + + // Add callers of imported functions to be analyzed + std::set added; + + for (const auto& [stub_addr, _] : stub_addr_to_constant_state_of_registers) + { + auto it = fmap.upper_bound(stub_addr); + + if (it == fmap.begin()) + { + continue; + } + + auto stub_func = std::prev(it); + + for (u32 caller : stub_func->second.callers) + { + ppu_function_ext& func = ::at32(fmap, caller); + + if (func.attr.none_of(ppu_attr::no_size) && !func.blocks.empty() && !added.contains(caller)) + { + added.emplace(caller); + func_queue.emplace_back(::at32(fmap, caller)); + } + } + } + + if (added.empty()) + { + break; + } + + is_function_caller_analysis = true; + } + if (check_aborted && check_aborted()) { return false; } - ppu_function_ext& func = func_queue[i].get(); + ppu_function_ext& func = func_queue[i]; // Fixup TOCs - if (func.toc && func.toc != umax) + if (!is_function_caller_analysis && func.toc && func.toc != umax) { // Fixup callers for (u32 addr : func.callers) @@ -1407,7 +1456,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const u32 iaddr = addr; const ppu_opcode_t op{get_ref(iaddr)}; - const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + const ppu_itype::type type = g_ppu_itype.decode(op.opcode); if (type == ppu_itype::B || type == ppu_itype::BC) { @@ -1453,7 +1502,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } } - if (func.blocks.empty()) + if (!is_function_caller_analysis && func.blocks.empty()) { // Special function analysis const vm::cptr _ptr = vm::cast(func.addr); @@ -1760,7 +1809,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con if (parent_block != umax) { // Inherit loaded registers mask (lazily) - block.mapped_registers_mask = ::at32(block_queue, parent_block).mapped_registers_mask; + block.mapped_registers_mask.mask = ::at32(block_queue, parent_block).mapped_registers_mask.mask; } return static_cast(block_queue.size() - 1); @@ -1769,6 +1818,15 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con return umax; }; + std::map preserve_blocks; + + if (is_function_caller_analysis) + { + preserve_blocks = std::move(func.blocks); + func.blocks.clear(); + func.blocks.emplace(preserve_blocks.begin()->first, 0); + } + for (auto& block : func.blocks) { if (!block.second && block.first < func_end) @@ -1813,7 +1871,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con auto is_reg_mapped = [&](u32 index) { - return !!(block_queue[j].mapped_registers_mask & (u64{1} << index)); + return !!(block_queue[j].mapped_registers_mask.mask & (u64{1} << index)); }; reg_state_t dummy_state{}; @@ -1824,7 +1882,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const usz reg_mask = u64{1} << index; - if (~block->moved_registers_mask & reg_mask) + if (~block->moved_registers_mask.mask & reg_mask) { if ((j + 1) * 64 >= reg_state_storage.size()) { @@ -1836,11 +1894,11 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con usz begin_block = umax; // Try searching for register origin - if (block->mapped_registers_mask & reg_mask) + if (block->mapped_registers_mask.mask & reg_mask) { for (u32 i = block->parent_block_idx; i != umax; i = block_queue[i].parent_block_idx) { - if (~block_queue[i].moved_registers_mask & reg_mask) + if (~block_queue[i].moved_registers_mask.mask & reg_mask) { continue; } @@ -1860,8 +1918,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con reg_state_storage[64 * j + index] = make_unknown_reg_state(); } - block->mapped_registers_mask |= reg_mask; - block->moved_registers_mask |= reg_mask; + block->mapped_registers_mask.mask |= reg_mask; + block->moved_registers_mask.mask |= reg_mask; } return reg_state_storage[64 * j + index]; @@ -1877,8 +1935,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con reg_state_storage[64 * block_index + index] = rhs; const usz reg_mask = u64{1} << index; - block_queue[block_index].mapped_registers_mask |= reg_mask; - block_queue[block_index].moved_registers_mask |= reg_mask; + block_queue[block_index].mapped_registers_mask.mask |= reg_mask; + block_queue[block_index].moved_registers_mask.mask |= reg_mask; }; const auto unmap_reg = [&](u32 index) @@ -1887,8 +1945,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const usz reg_mask = u64{1} << index; - block->mapped_registers_mask &= ~reg_mask; - block->moved_registers_mask &= ~reg_mask; + block->mapped_registers_mask.mask &= ~reg_mask; + block->moved_registers_mask.mask &= ~reg_mask; }; enum : u32 @@ -1907,7 +1965,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const u32 iaddr = _ptr.addr(); const ppu_opcode_t op{*advance(_ptr, ptr, 1)}; - const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + const ppu_itype::type type = g_ppu_itype.decode(op.opcode); switch (type) { @@ -1935,13 +1993,55 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const bool is_call = op.lk && target != iaddr && target != _ptr.addr() && _ptr.addr() < func_end; const auto pfunc = is_call ? &add_func(target, 0, 0) : nullptr; - if (pfunc && pfunc->blocks.empty()) + if (pfunc && pfunc->blocks.empty() && !is_function_caller_analysis) { // Postpone analysis (no info) postpone_analysis = true; break; } + if (is_function_caller_analysis && is_call && !(pfunc->attr & ppu_attr::no_return)) + { + while (is_function_caller_analysis) + { + // Verify that it is the call to the imported function (may be more than one) + const auto it = stub_addr_to_constant_state_of_registers.lower_bound(target); + + if (it == stub_addr_to_constant_state_of_registers.end()) + { + break; + } + + const auto next_func = fmap.upper_bound(it->first); + + if (next_func == fmap.begin()) + { + break; + } + + const auto stub_func = std::prev(next_func); + + if (stub_func->first == target) + { + // It is + // Now, mine register state + // Currently only of R3 + + if (is_reg_mapped(3)) + { + const reg_state_t& value = get_reg(3); + + if (value(is_const)) + { + it->second.emplace_back(ppua_reg_mask_t{ 1u << 3 }, value(minv) ); + } + } + } + + break; + } + } + // Add next block if necessary if ((is_call && !(pfunc->attr & ppu_attr::no_return)) || (type == ppu_itype::BC && (op.bo & 0x14) != 0x14)) { @@ -1993,7 +2093,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con store_block_reg(next_idx, lhs_cr_state, lhs_state); store_block_reg(next_idx, rhs_cr_state, rhs_state); - const u64 reg_mask = block_queue[j].mapped_registers_mask; + const u64 reg_mask = block_queue[j].mapped_registers_mask.mask; for (u32 bit = std::countr_zero(reg_mask); bit < 64 && reg_mask & (u64{1} << bit); bit += 1, bit = std::countr_zero(reg_mask >> (bit % 64)) + bit) @@ -2024,7 +2124,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con else if (is_call || target < func.addr || target >= func_end) { // Add function call (including obvious tail call) - add_func(target, 0, 0); + add_func(target, 0, func.addr); } else { @@ -2291,6 +2391,65 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con continue; } + case ppu_itype::LWZ: + { + const bool is_load_from_toc = (is_function_caller_analysis && op.ra == 2u && func.toc && func.toc != umax); + + if (is_load_from_toc || is_reg_mapped(op.rd) || is_reg_mapped(op.ra)) + { + const reg_state_t ra = get_reg(op.ra); + auto& rd = get_reg(op.rd); + + rd = {}; + rd.tag = reg_tag_allocator++; + rd.is_loaded = true; + + reg_state_t const_offs{}; + const_offs.load_const(op.simm16); + + reg_state_t toc_offset{}; + toc_offset.load_const(func.toc); + + const reg_state_t& off_ra = is_load_from_toc ? toc_offset : ra; + + rd.ge_than = const_offs(minv); + + const bool is_negative = const_offs(sign_bitv) == 1u; + + const bool is_offset_test_ok = is_negative + ? (0 - const_offs(minv) <= off_ra(minv) && off_ra(minv) + const_offs(minv) < segs_end) + : (off_ra(minv) < segs_end && const_offs(minv) < segs_end - off_ra(minv)); + + if (off_ra(minv) < off_ra(maxv) && is_offset_test_ok) + { + rd.ge_than += off_ra(minv); + + const bool is_range_end_test_ok = is_negative + ? (off_ra(maxv) + const_offs(minv) <= segs_end) + : (off_ra(maxv) - 1 < segs_end - 1 && const_offs(minv) <= segs_end - off_ra(maxv)); + + if (is_range_end_test_ok) + { + rd.value_range = off_ra.value_range; + } + } + + if (is_load_from_toc) + { + if (rd.value_range == 1) + { + // Try to load a constant value from data segment + if (auto val_ptr = get_ptr(static_cast(rd.ge_than))) + { + rd = {}; + rd.load_const(*val_ptr); + } + } + } + } + + continue; + } case ppu_itype::LWZX: case ppu_itype::LDX: // TODO: Confirm if LDX can appear in jumptable branching (probably in LV1 applications such as ps2_emu) { @@ -2311,6 +2470,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con // Register possible jumptable offset auto& rd = get_reg(op.rd); rd = {}; + rd.tag = reg_tag_allocator++; rd.is_loaded = true; const reg_state_t& const_reg = is_ra ? ra : rb; @@ -2451,6 +2611,19 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } } + if (!preserve_blocks.empty()) + { + ensure(func.blocks.size() == preserve_blocks.size()); + + for (auto fit = func.blocks.begin(), pit = preserve_blocks.begin(); fit != func.blocks.end(); fit++, pit++) + { + // Ensure block addresses match + ensure(fit->first == pit->first); + } + + func.blocks = std::move(preserve_blocks); + } + if (postpone_analysis) { // Block aborted: abort function, postpone @@ -2501,7 +2674,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const u32 iaddr = _ptr.addr(); const ppu_opcode_t op{get_ref(_ptr++)}; - const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + const ppu_itype::type type = g_ppu_itype.decode(op.opcode); if (type == ppu_itype::B || type == ppu_itype::BC) { @@ -2574,7 +2747,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const u32 addr = _ptr.addr(); const ppu_opcode_t op{get_ref(_ptr++)}; - const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + const ppu_itype::type type = g_ppu_itype.decode(op.opcode); if (type == ppu_itype::UNK) { @@ -2813,7 +2986,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const ppu_opcode_t op{get_ref(i_pos)}; - switch (auto type = s_ppu_itype.decode(op.opcode)) + switch (auto type = g_ppu_itype.decode(op.opcode)) { case ppu_itype::UNK: case ppu_itype::ECIWX: @@ -2884,7 +3057,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } const ppu_opcode_t test_op{get_ref(target)}; - const auto type0 = s_ppu_itype.decode(test_op.opcode); + const auto type0 = g_ppu_itype.decode(test_op.opcode); if (type0 == ppu_itype::UNK) { @@ -2906,7 +3079,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con break; } - const auto type1 = s_ppu_itype.decode(get_ref(target + 4)); + const auto type1 = g_ppu_itype.decode(get_ref(target + 4)); if (type1 == ppu_itype::UNK) { diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 5d9053847d..693ad8c7ea 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -106,6 +106,11 @@ struct ppu_segment void* ptr{}; }; +struct ppua_reg_mask_t +{ + u64 mask; +}; + // PPU Module Information template struct ppu_module : public Type @@ -138,6 +143,8 @@ struct ppu_module : public Type ppu_module* parent = nullptr; // For compilation: refers to original structure (is whole, not partitioned) std::pair local_bounds{0, u32{umax}}; // Module addresses range std::shared_ptr> jit_bounds; // JIT instance modules addresses range + std::unordered_map imports; // Imports information for release upon unload (TODO: OVL implementation!) + std::map>> stub_addr_to_constant_state_of_registers; // Tells possible constant states of registers of functions bool is_relocatable = false; // Is code relocatable(?) template diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index d95737ba50..2c3b958e87 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -71,6 +71,25 @@ extern u32 ppu_generate_id(std::string_view name) return result; } +static void select_from_nids_scenpdrm_addrs(std::map>>& result, const std::unordered_map& fnid_to_use_addr) +{ + static const u32 fnids_list[] = + { + ppu_generate_id("sceNpDrmProcessExitSpawn"), + ppu_generate_id("sceNpDrmProcessExitSpawn2"), + ppu_generate_id("sceNpDrmIsAvailable"), + ppu_generate_id("sceNpDrmIsAvailable2"), + }; + + for (const auto& [nid, use] : fnid_to_use_addr) + { + if (std::count(std::begin(fnids_list), std::end(fnids_list), nid)) + { + result.emplace(use, 0); + } + } +} + ppu_static_module::ppu_static_module(const char* name) : name(name) { @@ -157,9 +176,6 @@ struct ppu_linkage_info // FNID -> (export; [imports...]) std::map functions{}; std::map variables{}; - - // Obsolete - bool imported = false; }; // Module map @@ -940,9 +956,12 @@ static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_inf return result; } -static auto ppu_load_imports(const ppu_module& _module, std::vector& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end) +using import_result_t = std::pair, std::unordered_map>; + +static import_result_t ppu_load_imports(const ppu_module& _module, std::vector& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end) { - std::unordered_map result; + import_result_t result; + auto& [import_table, nid_to_use_addr] = result; std::lock_guard lock(link->mutex); @@ -976,12 +995,18 @@ static auto ppu_load_imports(const ppu_module& _module, std::vector 0x%x", module_name, ppu_get_function_name(module_name, fnid), fnid, fstub); // Function linkage info - auto& flink = link->modules[module_name].functions[fnid]; + auto& flink = mlink.functions[fnid]; // Add new import - result.emplace(faddr, &flink); + import_table.emplace(faddr, &flink); flink.imports.emplace(faddr); - mlink.imported = true; + + // Check address + // TODO: The address of use should be extracted from analyser instead + if (fstub && fstub >= _module.segs[0].addr && fstub <= _module.segs[0].addr + _module.segs[0].size) + { + nid_to_use_addr.emplace(fnid, fstub); + } // Link address (special HLE function by default) const u32 link_addr = flink.export_addr ? flink.export_addr : g_fxo->get().addr; @@ -992,7 +1017,7 @@ static auto ppu_load_imports(const ppu_module& _module, std::vector(fnids, i + lib.num_func) : 0) { - result.emplace(frefs, &flink); + import_table.emplace(frefs, &flink); flink.frefss.emplace(frefs); ppu_patch_refs(_module, &relocs, frefs, link_addr); } @@ -1010,12 +1035,11 @@ static auto ppu_load_imports(const ppu_module& _module, std::vectormodules[module_name].variables[vnid]; + auto& vlink = mlink.variables[vnid]; // Add new import - result.emplace(vref, &vlink); + import_table.emplace(vref, &vlink); vlink.imports.emplace(vref); - mlink.imported = true; // Link if available ppu_patch_refs(_module, &relocs, vref, vlink.export_addr); @@ -1838,10 +1862,13 @@ shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc); + std::unordered_map nid_to_use_addr; ppu_linkage_info dummy{}; prx->specials = ppu_load_exports(*prx, virtual_load ? &dummy : &link, prx->exports_start, prx->exports_end, true, &exported_funcs); - prx->imports = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end); + + std::tie(prx->imports, nid_to_use_addr) = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end); + select_from_nids_scenpdrm_addrs(prx->stub_addr_to_constant_state_of_registers, nid_to_use_addr); if (virtual_load) { @@ -2450,10 +2477,13 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str return false; } + std::unordered_map nid_to_use_addr; 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::tie(std::ignore, nid_to_use_addr) = ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); + select_from_nids_scenpdrm_addrs(_main.stub_addr_to_constant_state_of_registers, nid_to_use_addr); std::stable_sort(_main.relocs.begin(), _main.relocs.end()); } @@ -3061,10 +3091,14 @@ std::pair, CellError> ppu_load_overlay(const ppu_exec_ob fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic); } + std::unordered_map nid_to_use_addr; 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); + + std::tie(std::ignore, nid_to_use_addr) = ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); + select_from_nids_scenpdrm_addrs(ovlm->stub_addr_to_constant_state_of_registers, nid_to_use_addr); + } break; } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 65467b168b..90241016e6 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4162,6 +4162,52 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector(g_cfg.core.llvm_threads ? g_cfg.core.llvm_threads : u32{umax}, ::size32(file_queue)); const u32 cpu_thread_limit = utils::get_thread_count() > 8u ? std::max(utils::get_thread_count(), 2) - 1 : utils::get_thread_count(); // One LLVM thread less + std::vector decrypt_klics; + + if (loaded_modules) + { + for (auto mod : *loaded_modules) + { + for (const auto& [stub, data_vec] : mod->stub_addr_to_constant_state_of_registers) + { + if (decrypt_klics.size() >= 4u) + { + break; + } + + for (const auto& [reg_mask, constant_value] : data_vec) + { + if (decrypt_klics.size() >= 4u) + { + break; + } + + if (constant_value > u32{umax}) + { + continue; + } + + // R3 - first argument + if (reg_mask.mask & (1u << 3)) + { + // Sizeof KLIC + if (auto klic_ptr = mod->get_ptr(static_cast(constant_value), 16)) + { + // Try to read from that address + if (const u128 klic_value = read_from_ptr(klic_ptr)) + { + if (!std::count_if(decrypt_klics.begin(), decrypt_klics.end(), FN(std::memcmp(&x, &klic_value, 16) == 0))) + { + decrypt_klics.emplace_back(klic_value); + } + } + } + } + } + } + } + } + named_thread_group workers("SPRX Worker ", std::min(software_thread_limit, cpu_thread_limit), [&] { #ifdef __APPLE__ @@ -4211,12 +4257,48 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector decrypt_klics.size()) + { + src.close(); + break; + } + + // Some files may fail to decrypt due to the lack of klic + u128 key = i == decrypt_klics.size() ? u128{} : decrypt_klics[i]; + + if (auto result = decrypt_self(src, i == decrypt_klics.size() ? nullptr : reinterpret_cast(&key))) + { + src = std::move(result); + break; + } + } + + if (!src && !Emu.klic.empty() && src.open(path)) + { + src = decrypt_self(src, reinterpret_cast(&Emu.klic[0])); + + if (src) + { + ppu_log.error("Possible missed KLIC for precompilation of '%s', please report to developers.", path); + + // Ignore executables larger than 500KB to prevent a long pause on exitspawn + if (src.size() >= 500000) + { + g_progr_ftotal_bits -= file_size; + + continue; + } + } + } if (!src) { ppu_log.notice("Failed to decrypt '%s'", path); + + g_progr_ftotal_bits -= file_size; + continue; } @@ -4380,12 +4462,40 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector decrypt_klics.size()) + { + src.close(); + break; + } + + // Some files may fail to decrypt due to the lack of klic + u128 key = i == decrypt_klics.size() ? u128{} : decrypt_klics[i]; + + if (auto result = decrypt_self(src, i == decrypt_klics.size() ? nullptr : reinterpret_cast(&key))) + { + src = std::move(result); + break; + } + } + + if (!src && !Emu.klic.empty() && src.open(path)) + { + src = decrypt_self(src, reinterpret_cast(&Emu.klic[0])); + + if (src) + { + ppu_log.error("Possible missed KLIC for precompilation of '%s', please report to developers.", path); + } + } if (!src) { ppu_log.notice("Failed to decrypt '%s'", path); + + g_progr_ftotal_bits -= file_size; + continue; } @@ -4484,6 +4594,7 @@ extern void ppu_initialize() } std::vector*> module_list; + module_list.emplace_back(&g_fxo->get>()); const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/"); diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 9f94ad50f6..0e4ec1bb1a 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -382,14 +382,15 @@ public: static_assert(ppu_join_status::max <= ppu_join_status{ppu_thread::id_base}); -template +template struct ppu_gpr_cast_impl { static_assert(!sizeof(T), "Invalid type for ppu_gpr_cast<>"); }; -template -struct ppu_gpr_cast_impl || std::is_enum_v>> +template + requires std::is_integral_v || std::is_enum_v +struct ppu_gpr_cast_impl { static_assert(sizeof(T) <= 8, "Too big integral type for ppu_gpr_cast<>()"); static_assert(std::is_same_v, bool> == false, "bool type is deprecated in ppu_gpr_cast<>(), use b8 instead"); @@ -405,8 +406,8 @@ struct ppu_gpr_cast_impl || std::is_en } }; -template<> -struct ppu_gpr_cast_impl +template <> +struct ppu_gpr_cast_impl { static inline u64 to(const b8& value) { @@ -419,8 +420,8 @@ struct ppu_gpr_cast_impl } }; -template -struct ppu_gpr_cast_impl, void> +template +struct ppu_gpr_cast_impl> { static inline u64 to(const vm::_ptr_base& value) { @@ -433,8 +434,8 @@ struct ppu_gpr_cast_impl, void> } }; -template -struct ppu_gpr_cast_impl, void> +template +struct ppu_gpr_cast_impl> { static inline u64 to(const vm::_ref_base& value) { @@ -448,7 +449,7 @@ struct ppu_gpr_cast_impl, void> }; template <> -struct ppu_gpr_cast_impl +struct ppu_gpr_cast_impl { static inline u64 to(const vm::null_t& /*value*/) { diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 72f589192e..469d52d43c 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -6234,14 +6234,14 @@ public: const value_t ab[2]{a, b}; std::bitset<2> safe_int_compare(0); - std::bitset<2> safe_nonzero_compare(0); + std::bitset<2> safe_finite_compare(0); for (u32 i = 0; i < 2; i++) { if (auto [ok, data] = get_const_vector(ab[i].value, m_pos, __LINE__ + i); ok) { safe_int_compare.set(i); - safe_nonzero_compare.set(i); + safe_finite_compare.set(i); for (u32 j = 0; j < 4; j++) { @@ -6256,7 +6256,7 @@ public: // we don't produce "extended range" values the same way as real hardware, it's not safe to apply // this optimization for values outside of the range of x86 floating point hardware. safe_int_compare.reset(i); - if (!exponent) safe_nonzero_compare.reset(i); + if ((value & 0x7fffffffu) >= 0x7f7ffffeu) safe_finite_compare.reset(i); } } } @@ -6267,17 +6267,20 @@ public: return eval(sext(bitcast(a) > bitcast(b))); } + if (safe_finite_compare.test(1)) + { + return eval(sext(fcmp_uno(clamp_negative_smax(a) > b))); + } + + if (safe_finite_compare.test(0)) + { + return eval(sext(fcmp_ord(a > clamp_smax(b)))); + } + const auto ai = eval(bitcast(a)); const auto bi = eval(bitcast(b)); - if (!safe_nonzero_compare.any()) - { - return eval(sext(fcmp_uno(a != b) & select((ai & bi) >= 0, ai > bi, ai < bi))); - } - else - { - return eval(sext(select((ai & bi) >= 0, ai > bi, ai < bi))); - } + return eval(sext(fcmp_uno(a != b) & select((ai & bi) >= 0, ai > bi, ai < bi))); }); set_vr(op.rt, fcgt(get_vr(op.ra), get_vr(op.rb))); diff --git a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp index 66481ed280..33475a8553 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(); - src = decrypt_self(std::move(src), reinterpret_cast(&klic), nullptr, true); + src = decrypt_self(std::move(src), reinterpret_cast(&klic)); if (!src) { diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 21a4f55000..046d19c48e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -265,7 +265,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptrget().last_key(); - src = decrypt_self(std::move(src), reinterpret_cast(&klic), nullptr, true); + src = decrypt_self(std::move(src), reinterpret_cast(&klic)); if (!src) { diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.h b/rpcs3/Emu/Cell/lv2/sys_prx.h index 610ed68145..a78542dcbd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.h +++ b/rpcs3/Emu/Cell/lv2/sys_prx.h @@ -192,7 +192,6 @@ struct lv2_prx final : ppu_module shared_mutex mutex; std::unordered_map specials; - std::unordered_map imports; vm::ptr argv)> start = vm::null; vm::ptr argv)> stop = vm::null; diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 3884e439b2..b3c898c23b 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -26,7 +26,7 @@ template concept IdmBaseCompatible = (std::is_final_v ? IdmCompatible : !!(requires () { u32{T::id_step}, u32{T::id_count}; })); template -concept IdmSavable = IdmBaseCompatible && T::savestate_init_pos != 0 && (requires () { std::declval().save(std::declval>()); }); +concept IdmSavable = IdmBaseCompatible && T::savestate_init_pos != 0 && (requires(T& t, utils::serial& ar) { t.save(stx::exact_t(ar)); }); // If id_base is declared in base type, than storage type must declare id_type template @@ -105,7 +105,7 @@ namespace id_manager } // ID traits - template + template struct id_traits_load_func { static constexpr pointer_keeper(*load)(utils::serial&) = [](utils::serial& ar) -> pointer_keeper @@ -126,7 +126,8 @@ namespace id_manager }; template - struct id_traits_load_func> + requires requires() { &T::load; } + struct id_traits_load_func { static constexpr pointer_keeper(*load)(utils::serial&) = [](utils::serial& ar) -> pointer_keeper { @@ -134,14 +135,15 @@ namespace id_manager }; }; - template + template struct id_traits_savable_func { static constexpr bool(*savable)(void*) = [](void*) -> bool { return true; }; }; template - struct id_traits_savable_func> + requires requires { &T::savable; } + struct id_traits_savable_func { static constexpr bool(*savable)(void* ptr) = [](void* ptr) -> bool { return static_cast(ptr)->savable(); }; }; diff --git a/rpcs3/Emu/Memory/vm_ptr.h b/rpcs3/Emu/Memory/vm_ptr.h index eb70ee8f13..5081e0701d 100644 --- a/rpcs3/Emu/Memory/vm_ptr.h +++ b/rpcs3/Emu/Memory/vm_ptr.h @@ -342,21 +342,24 @@ namespace vm template using bcpptr = bpptr; // Perform static_cast (for example, vm::ptr to vm::ptr) - template*>(std::declval()))> + template + requires requires(T* t) { static_cast*>(t); } inline _ptr_base, u32> static_ptr_cast(const _ptr_base& other) { return vm::cast(other.addr()); } // Perform const_cast (for example, vm::cptr to vm::ptr) - template*>(std::declval()))> + template + requires requires(T* t) { const_cast*>(t); } inline _ptr_base, u32> const_ptr_cast(const _ptr_base& other) { return vm::cast(other.addr()); } // Perform reinterpret cast - template *>(std::declval()))> + template + requires requires(T* t) { reinterpret_cast*>(t); } inline _ptr_base, u32> unsafe_ptr_cast(const _ptr_base& other) { return vm::cast(other.addr()); @@ -426,8 +429,8 @@ struct to_se, Se> }; // Format pointer -template -struct fmt_unveil, void> +template +struct fmt_unveil> { using type = vm::_ptr_base; // Use only T, ignoring AT @@ -438,37 +441,37 @@ struct fmt_unveil, void> }; template <> -struct fmt_class_string, void> +struct fmt_class_string> { static void format(std::string& out, u64 arg); }; template -struct fmt_class_string, void> : fmt_class_string, void> +struct fmt_class_string> : fmt_class_string> { // Classify all pointers as const void* }; template <> -struct fmt_class_string, void> +struct fmt_class_string> { static void format(std::string& out, u64 arg); }; template <> -struct fmt_class_string, void> : fmt_class_string> +struct fmt_class_string> : fmt_class_string> { // Classify char* as const char* }; template -struct fmt_class_string, void> : fmt_class_string> +struct fmt_class_string> : fmt_class_string> { // Classify const char[] as const char* }; template -struct fmt_class_string, void> : fmt_class_string> +struct fmt_class_string> : fmt_class_string> { // Classify char[] as const char* }; diff --git a/rpcs3/Emu/Memory/vm_ref.h b/rpcs3/Emu/Memory/vm_ref.h index 5abded43b0..59e6daa8f0 100644 --- a/rpcs3/Emu/Memory/vm_ref.h +++ b/rpcs3/Emu/Memory/vm_ref.h @@ -193,8 +193,8 @@ struct to_se, Se> }; // Forbid formatting -template -struct fmt_unveil, void> +template +struct fmt_unveil> { static_assert(!sizeof(T), "vm::_ref_base<>: ambiguous format argument"); }; diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index f2bc571e5f..382ef9cc06 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -500,12 +500,13 @@ struct copy_rgb655_block_swizzled struct copy_decoded_bc1_block { - static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u16 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) + static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u32 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) { u32 src_offset = 0, dst_offset = 0, destinationPitch = dst_pitch_in_block * 4; for (u32 row = 0; row < row_count * depth; row++) { - for (u32 col = 0; col < width_in_block; col++) { + for (u32 col = 0; col < width_in_block; col++) + { const u8* compressedBlock = reinterpret_cast(&src[src_offset + col]); u8* decompressedBlock = reinterpret_cast(&dst[dst_offset + col * 4]); bcdec_bc1(compressedBlock, decompressedBlock, destinationPitch); @@ -519,12 +520,13 @@ struct copy_decoded_bc1_block struct copy_decoded_bc2_block { - static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u16 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) + static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u32 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) { u32 src_offset = 0, dst_offset = 0, destinationPitch = dst_pitch_in_block * 4; for (u32 row = 0; row < row_count * depth; row++) { - for (u32 col = 0; col < width_in_block; col++) { + for (u32 col = 0; col < width_in_block; col++) + { const u8* compressedBlock = reinterpret_cast(&src[src_offset + col]); u8* decompressedBlock = reinterpret_cast(&dst[dst_offset + col * 4]); bcdec_bc2(compressedBlock, decompressedBlock, destinationPitch); @@ -538,12 +540,13 @@ struct copy_decoded_bc2_block struct copy_decoded_bc3_block { - static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u16 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) + static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u32 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) { u32 src_offset = 0, dst_offset = 0, destinationPitch = dst_pitch_in_block * 4; for (u32 row = 0; row < row_count * depth; row++) { - for (u32 col = 0; col < width_in_block; col++) { + for (u32 col = 0; col < width_in_block; col++) + { const u8* compressedBlock = reinterpret_cast(&src[src_offset + col]); u8* decompressedBlock = reinterpret_cast(&dst[dst_offset + col * 4]); bcdec_bc3(compressedBlock, decompressedBlock, destinationPitch); diff --git a/rpcs3/Emu/RSX/Common/expected.hpp b/rpcs3/Emu/RSX/Common/expected.hpp index 39254c3f34..d3da0a402d 100644 --- a/rpcs3/Emu/RSX/Common/expected.hpp +++ b/rpcs3/Emu/RSX/Common/expected.hpp @@ -94,11 +94,11 @@ namespace rsx return value; } - template>> - operator bool() const - { - return error.empty(); - } + operator bool() const + requires(!std::is_same_v) + { + return error.empty(); + } operator std::pair() const { diff --git a/rpcs3/Emu/RSX/Common/texture_cache_types.cpp b/rpcs3/Emu/RSX/Common/texture_cache_types.cpp new file mode 100644 index 0000000000..4a90f27198 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/texture_cache_types.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "texture_cache_types.h" +#include "Emu/system_config.h" + +namespace rsx +{ + void invalidation_cause::flag_bits_from_cause(enum_type cause) + { + constexpr const std::array s_lookup_table + { + std::make_pair(enum_type::read, flags::cause_is_read), + std::make_pair(enum_type::deferred_read, flags::cause_is_read | flags::cause_is_deferred), + std::make_pair(enum_type::write, flags::cause_is_write), + std::make_pair(enum_type::deferred_write, flags::cause_is_write | flags::cause_is_deferred), + std::make_pair(enum_type::unmap, flags::cause_keeps_fault_range_protection | flags::cause_skips_flush), + std::make_pair(enum_type::reprotect, flags::cause_keeps_fault_range_protection), + std::make_pair(enum_type::superseded_by_fbo, flags::cause_keeps_fault_range_protection | flags::cause_skips_fbos | flags::cause_skips_flush), + std::make_pair(enum_type::committed_as_fbo, flags::cause_skips_fbos), + }; + + m_flag_bits = 0; + for (const auto& entry : s_lookup_table) + { + if (entry.first == cause) + { + m_flag_bits = entry.second | flags::cause_is_valid; + break; + } + } + + if (cause == enum_type::superseded_by_fbo && + g_cfg.video.strict_texture_flushing) [[ unlikely ]] + { + m_flag_bits &= ~flags::cause_skips_flush; + } + } +} diff --git a/rpcs3/Emu/RSX/Common/texture_cache_types.h b/rpcs3/Emu/RSX/Common/texture_cache_types.h index 40b9f39215..77ba88d0b7 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_types.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_types.h @@ -1,7 +1,5 @@ #pragma once -#include "Emu/system_config.h" - namespace rsx { /** @@ -131,35 +129,6 @@ namespace rsx private: u32 m_flag_bits = 0; - void flag_bits_from_cause(enum_type cause) - { - constexpr std::array s_lookup_table - { - std::make_pair(enum_type::read, flags::cause_is_read), - std::make_pair(enum_type::deferred_read, flags::cause_is_read | flags::cause_is_deferred), - std::make_pair(enum_type::write, flags::cause_is_write), - std::make_pair(enum_type::deferred_write, flags::cause_is_write | flags::cause_is_deferred), - std::make_pair(enum_type::unmap, flags::cause_keeps_fault_range_protection | flags::cause_skips_flush), - std::make_pair(enum_type::reprotect, flags::cause_keeps_fault_range_protection), - std::make_pair(enum_type::superseded_by_fbo, flags::cause_keeps_fault_range_protection | flags::cause_skips_fbos | flags::cause_skips_flush), - std::make_pair(enum_type::committed_as_fbo, flags::cause_skips_fbos), - }; - - m_flag_bits = 0; - for (const auto& entry : s_lookup_table) - { - if (entry.first == cause) - { - m_flag_bits = entry.second | flags::cause_is_valid; - break; - } - } - - if (cause == enum_type::superseded_by_fbo && - g_cfg.video.strict_texture_flushing) [[ unlikely ]] - { - m_flag_bits &= ~flags::cause_skips_flush; - } - } + void flag_bits_from_cause(enum_type cause); }; } diff --git a/rpcs3/Emu/RSX/GSFrameBase.cpp b/rpcs3/Emu/RSX/GSFrameBase.cpp new file mode 100644 index 0000000000..32121bf06e --- /dev/null +++ b/rpcs3/Emu/RSX/GSFrameBase.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" +#include "GSFrameBase.h" +#include "Emu/system_config.h" + +atomic_t g_game_window_focused = false; + +bool is_input_allowed() +{ + return g_game_window_focused || g_cfg.io.background_input_enabled; +} diff --git a/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp b/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp index 19cb43c970..1082ae41b9 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp @@ -462,7 +462,7 @@ namespace rsx } void swizzled_copy_2( - u8* linear_pixels, + const u8* linear_pixels, u8* swizzled_pixels, u32 linear_pitch, u16 out_w, @@ -480,14 +480,14 @@ namespace rsx sw_height_log2 = sw_height_log2 == 0 ? 1 : sw_height_log2; // swizzle based on destination size - u16 sw_width = 1 << sw_width_log2; - u16 sw_height = 1 << sw_height_log2; + const u16 sw_width = 1 << sw_width_log2; + const u16 sw_height = 1 << sw_height_log2; */ std::vector sw_temp; - u32 sw_width = next_pow2(out_w); - u32 sw_height = next_pow2(out_h); + const u32 sw_width = next_pow2(out_w); + const u32 sw_height = next_pow2(out_h); // Check and pad texture out if we are given non power of 2 output if (sw_width != out_w || sw_height != out_h) @@ -641,7 +641,7 @@ namespace rsx } // Swizzle_copy_2 only pads the data and encodes it as a swizzled output. Transformation (scaling, rotation, etc) is done in swizzle_copy_1 - swizzled_copy_2(const_cast(pixels_src), dst.pixels, src_pitch, out_w, out_h, dst.bpp); + swizzled_copy_2(pixels_src, dst.pixels, src_pitch, out_w, out_h, dst.bpp); } if (tiled_region) diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index ae7edebe48..ccba75b985 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "ProgramStateCache.h" #include "Emu/system_config.h" +#include "util/sysinfo.hpp" #include @@ -21,31 +22,119 @@ #endif #endif +#ifdef ARCH_ARM64 +#define AVX512_ICL_FUNC +#endif + +#ifdef _MSC_VER +#define AVX512_ICL_FUNC +#else +#define AVX512_ICL_FUNC __attribute__((__target__("avx512f,avx512bw,avx512dq,avx512cd,avx512vl,avx512bitalg,avx512ifma,avx512vbmi,avx512vbmi2,avx512vnni,avx512vpopcntdq"))) +#endif + + using namespace program_hash_util; -usz vertex_program_utils::get_vertex_program_ucode_hash(const RSXVertexProgram &program) +AVX512_ICL_FUNC usz vertex_program_utils::get_vertex_program_ucode_hash(const RSXVertexProgram &program) { - // Checksum as hash with rotated data - const void* instbuffer = program.data.data(); - u32 instIndex = 0; - usz acc0 = 0; - usz acc1 = 0; - - do +#ifdef ARCH_X64 + if (utils::has_avx512_icl()) { - if (program.instruction_mask[instIndex]) + // Load all elements of the instruction_mask bitset + const __m512i* instMask512 = reinterpret_cast(&program.instruction_mask); + const __m128i* instMask128 = reinterpret_cast(&program.instruction_mask); + + const __m512i lowerMask = _mm512_loadu_si512(instMask512); + const __m128i upper128 = _mm_loadu_si128(instMask128 + 4); + const __m512i upperMask = _mm512_zextsi128_si512(upper128); + + __m512i maskIndex = _mm512_setzero_si512(); + const __m512i negativeOnes = _mm512_set1_epi64(-1); + + // Special masks to test against bitset + const __m512i testMask0 = _mm512_set_epi64( + 0x0808080808080808, + 0x0808080808080808, + 0x0404040404040404, + 0x0404040404040404, + 0x0202020202020202, + 0x0202020202020202, + 0x0101010101010101, + 0x0101010101010101); + + const __m512i testMask1 = _mm512_set_epi64( + 0x8080808080808080, + 0x8080808080808080, + 0x4040404040404040, + 0x4040404040404040, + 0x2020202020202020, + 0x2020202020202020, + 0x1010101010101010, + 0x1010101010101010); + + const __m512i* instBuffer = reinterpret_cast(program.data.data()); + __m512i acc0 = _mm512_setzero_si512(); + __m512i acc1 = _mm512_setzero_si512(); + + __m512i rotMask0 = _mm512_set_epi64(7, 6, 5, 4, 3, 2, 1, 0); + __m512i rotMask1 = _mm512_set_epi64(15, 14, 13, 12, 11, 10, 9, 8); + __m512i rotMaskAdd = _mm512_set_epi64(16, 16, 16, 16, 16, 16, 16, 16); + + u32 instIndex = 0; + + // If there is remainder, add an extra (masked) iteration + u32 extraIteration = (program.data.size() % 32 != 0) ? 1 : 0; + u32 length = (program.data.size() / 32) + extraIteration; + + // The instruction mask will prevent us from reading out of bounds, we do not need a seperate masked loop + // for the remainder, or a scalar loop. + while (instIndex < (length)) { - const auto inst = v128::loadu(instbuffer, instIndex); - usz tmp0 = std::rotr(inst._u64[0], instIndex * 2); - acc0 += tmp0; - usz tmp1 = std::rotr(inst._u64[1], (instIndex * 2) + 1); - acc1 += tmp1; + const __m512i masks = _mm512_permutex2var_epi8(lowerMask, maskIndex, upperMask); + const __mmask8 result0 = _mm512_test_epi64_mask(masks, testMask0); + const __mmask8 result1 = _mm512_test_epi64_mask(masks, testMask1); + const __m512i load0 = _mm512_maskz_loadu_epi64(result0, (instBuffer + instIndex * 2)); + const __m512i load1 = _mm512_maskz_loadu_epi64(result1, (instBuffer + (instIndex * 2)+ 1)); + + const __m512i rotated0 = _mm512_rorv_epi64(load0, rotMask0); + const __m512i rotated1 = _mm512_rorv_epi64(load1, rotMask1); + + acc0 = _mm512_add_epi64(acc0, rotated0); + acc1 = _mm512_add_epi64(acc1, rotated1); + + rotMask0 = _mm512_add_epi64(rotMask0, rotMaskAdd); + rotMask1 = _mm512_add_epi64(rotMask1, rotMaskAdd); + maskIndex = _mm512_sub_epi8(maskIndex, negativeOnes); + + instIndex++; } - instIndex++; - } while (instIndex < (program.data.size() / 4)); + const __m512i result = _mm512_add_epi64(acc0, acc1); + return _mm512_reduce_add_epi64(result); + } +#endif + + // Checksum as hash with rotated data + const void* instbuffer = program.data.data(); + u32 instIndex = 0; + usz acc0 = 0; + usz acc1 = 0; + + do + { + if (program.instruction_mask[instIndex]) + { + const auto inst = v128::loadu(instbuffer, instIndex); + usz tmp0 = std::rotr(inst._u64[0], instIndex * 2); + acc0 += tmp0; + usz tmp1 = std::rotr(inst._u64[1], (instIndex * 2) + 1); + acc1 += tmp1; + } + + instIndex++; + } while (instIndex < (program.data.size() / 4)); return acc0 + acc1; -} + } vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vertex_program(const u32* data, u32 entry, RSXVertexProgram& dst_prog) { @@ -350,7 +439,7 @@ usz vertex_program_storage_hash::operator()(const RSXVertexProgram &program) con return rpcs3::hash64(ucode_hash, metadata_hash); } -bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const RSXVertexProgram &binary2) const +AVX512_ICL_FUNC bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const RSXVertexProgram &binary2) const { if (binary1.output_mask != binary2.output_mask) return false; @@ -363,10 +452,88 @@ bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const R if (binary1.jump_table != binary2.jump_table) return false; +#ifdef ARCH_X64 + if (utils::has_avx512_icl()) + { + // Load all elements of the instruction_mask bitset + const __m512i* instMask512 = reinterpret_cast(&binary1.instruction_mask); + const __m128i* instMask128 = reinterpret_cast(&binary1.instruction_mask); + + const __m512i lowerMask = _mm512_loadu_si512(instMask512); + const __m128i upper128 = _mm_loadu_si128(instMask128 + 4); + const __m512i upperMask = _mm512_zextsi128_si512(upper128); + + __m512i maskIndex = _mm512_setzero_si512(); + const __m512i negativeOnes = _mm512_set1_epi64(-1); + + // Special masks to test against bitset + const __m512i testMask0 = _mm512_set_epi64( + 0x0808080808080808, + 0x0808080808080808, + 0x0404040404040404, + 0x0404040404040404, + 0x0202020202020202, + 0x0202020202020202, + 0x0101010101010101, + 0x0101010101010101); + + const __m512i testMask1 = _mm512_set_epi64( + 0x8080808080808080, + 0x8080808080808080, + 0x4040404040404040, + 0x4040404040404040, + 0x2020202020202020, + 0x2020202020202020, + 0x1010101010101010, + 0x1010101010101010); + + const __m512i* instBuffer1 = reinterpret_cast(binary1.data.data()); + const __m512i* instBuffer2 = reinterpret_cast(binary2.data.data()); + + // If there is remainder, add an extra (masked) iteration + u32 extraIteration = (binary1.data.size() % 32 != 0) ? 1 : 0; + u32 length = (binary1.data.size() / 32) + extraIteration; + + u32 instIndex = 0; + + // The instruction mask will prevent us from reading out of bounds, we do not need a seperate masked loop + // for the remainder, or a scalar loop. + while (instIndex < (length)) + { + const __m512i masks = _mm512_permutex2var_epi8(lowerMask, maskIndex, upperMask); + + const __mmask8 result0 = _mm512_test_epi64_mask(masks, testMask0); + const __mmask8 result1 = _mm512_test_epi64_mask(masks, testMask1); + + const __m512i load0 = _mm512_maskz_loadu_epi64(result0, (instBuffer1 + (instIndex * 2))); + const __m512i load1 = _mm512_maskz_loadu_epi64(result0, (instBuffer2 + (instIndex * 2))); + const __m512i load2 = _mm512_maskz_loadu_epi64(result1, (instBuffer1 + (instIndex * 2) + 1)); + const __m512i load3 = _mm512_maskz_loadu_epi64(result1, (instBuffer2 + (instIndex * 2)+ 1)); + + const __mmask8 res0 = _mm512_cmpneq_epi64_mask(load0, load1); + const __mmask8 res1 = _mm512_cmpneq_epi64_mask(load2, load3); + + const u8 result = _kortestz_mask8_u8(res0, res1); + + //kortestz will set result to 1 if all bits are zero, so invert the check for result + if (!result) + { + return false; + } + + maskIndex = _mm512_sub_epi8(maskIndex, negativeOnes); + + instIndex++; + } + + return true; + } +#endif + const void* instBuffer1 = binary1.data.data(); const void* instBuffer2 = binary2.data.data(); usz instIndex = 0; - for (unsigned i = 0; i < binary1.data.size() / 4; i++) + while (instIndex < (binary1.data.size() / 4)) { if (binary1.instruction_mask[instIndex]) { diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 028aa6b178..eaf9cc4a44 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -4,9 +4,9 @@ #include "vkutils/descriptors.h" #include "vkutils/data_heap.h" -#include "vkutils/instance.hpp" +#include "vkutils/instance.h" #include "vkutils/sync.h" -#include "vkutils/swapchain.hpp" +#include "vkutils/swapchain.h" #include "VKGSRenderTypes.hpp" #include "VKTextureCache.h" diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 207c1155c3..5391e1308e 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -1,5 +1,5 @@ #include "device.h" -#include "instance.hpp" +#include "instance.h" #include "util/logs.hpp" #include "Emu/system_config.h" diff --git a/rpcs3/Emu/RSX/VK/vkutils/instance.cpp b/rpcs3/Emu/RSX/VK/vkutils/instance.cpp new file mode 100644 index 0000000000..0527376e08 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/instance.cpp @@ -0,0 +1,366 @@ +#include "stdafx.h" +#include "instance.h" + +namespace vk +{ + // Supported extensions + supported_extensions::supported_extensions(enumeration_class _class, const char* layer_name, VkPhysicalDevice pdev) + { + u32 count; + if (_class == enumeration_class::instance) + { + if (vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr) != VK_SUCCESS) + return; + } + else + { + ensure(pdev); + if (vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, nullptr) != VK_SUCCESS) + return; + } + + m_vk_exts.resize(count); + if (_class == enumeration_class::instance) + { + vkEnumerateInstanceExtensionProperties(layer_name, &count, m_vk_exts.data()); + } + else + { + vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, m_vk_exts.data()); + } + } + + bool supported_extensions::is_supported(std::string_view ext) const + { + return std::any_of(m_vk_exts.cbegin(), m_vk_exts.cend(), [&](const VkExtensionProperties& p) { return p.extensionName == ext; }); + } + + // Instance + instance::~instance() + { + if (m_instance) + { + destroy(); + } + } + + void instance::destroy() + { + if (!m_instance) return; + + if (m_debugger) + { + _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); + m_debugger = nullptr; + } + + if (m_surface) + { + vkDestroySurfaceKHR(m_instance, m_surface, nullptr); + m_surface = VK_NULL_HANDLE; + } + + vkDestroyInstance(m_instance, nullptr); + m_instance = VK_NULL_HANDLE; + } + + void instance::enable_debugging() + { + if (!g_cfg.video.debug_output) return; + + PFN_vkDebugReportCallbackEXT callback = vk::dbgFunc; + + _vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT")); + _vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkDestroyDebugReportCallbackEXT")); + + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {}; + dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + dbgCreateInfo.pfnCallback = callback; + dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + CHECK_RESULT(_vkCreateDebugReportCallback(m_instance, &dbgCreateInfo, NULL, &m_debugger)); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + bool instance::create(const char* app_name, bool fast) + { + // Initialize a vulkan instance + VkApplicationInfo app = {}; + + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pApplicationName = app_name; + app.applicationVersion = 0; + app.pEngineName = app_name; + app.engineVersion = 0; + app.apiVersion = VK_API_VERSION_1_0; + + // Set up instance information + + std::vector extensions; + std::vector layers; + const void* next_info = nullptr; + +#ifdef __APPLE__ + // Declare MVK variables here to ensure the lifetime within the entire scope + const VkBool32 setting_true = VK_TRUE; + const int32_t setting_fast_math = g_cfg.video.disable_msl_fast_math.get() ? MVK_CONFIG_FAST_MATH_NEVER : MVK_CONFIG_FAST_MATH_ON_DEMAND; + + std::vector mvk_settings; + VkLayerSettingsCreateInfoEXT mvk_layer_settings_create_info{}; +#endif + + if (!fast) + { + extensions_loaded = true; + supported_extensions support(supported_extensions::instance); + + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + if (support.is_supported(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) + { + extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + } + + if (support.is_supported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + } + +#ifdef __APPLE__ + if (support.is_supported(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME)) + { + extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME); + layers.push_back(kMVKMoltenVKDriverLayerName); + + mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_RESUME_LOST_DEVICE", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &setting_true }); + mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_FAST_MATH_ENABLED", VK_LAYER_SETTING_TYPE_INT32_EXT, 1, &setting_fast_math }); + + mvk_layer_settings_create_info.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT; + mvk_layer_settings_create_info.pNext = next_info; + mvk_layer_settings_create_info.settingCount = static_cast(mvk_settings.size()); + mvk_layer_settings_create_info.pSettings = mvk_settings.data(); + + next_info = &mvk_layer_settings_create_info; + } +#endif + + if (support.is_supported(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); + } + + if (support.is_supported(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); + } + + if (g_cfg.video.renderdoc_compatiblity && support.is_supported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) + { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + +#ifdef _WIN32 + extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); +#elif defined(__APPLE__) + extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); +#else + bool found_surface_ext = false; +#ifdef HAVE_X11 + if (support.is_supported(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + found_surface_ext = true; + } +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + if (support.is_supported(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + found_surface_ext = true; + } +#endif //(WAYLAND) + if (!found_surface_ext) + { + rsx_log.error("Could not find a supported Vulkan surface extension"); + return 0; + } +#endif //(WIN32, __APPLE__) + if (g_cfg.video.debug_output) + layers.push_back("VK_LAYER_KHRONOS_validation"); + } + + VkInstanceCreateInfo instance_info = {}; + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_info.pApplicationInfo = &app; + instance_info.enabledLayerCount = static_cast(layers.size()); + instance_info.ppEnabledLayerNames = layers.data(); + instance_info.enabledExtensionCount = fast ? 0 : static_cast(extensions.size()); + instance_info.ppEnabledExtensionNames = fast ? nullptr : extensions.data(); + instance_info.pNext = next_info; + + if (VkResult result = vkCreateInstance(&instance_info, nullptr, &m_instance); result != VK_SUCCESS) + { + if (result == VK_ERROR_LAYER_NOT_PRESENT) + { + rsx_log.fatal("Could not initialize layer VK_LAYER_KHRONOS_validation"); + } + + return false; + } + + return true; + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + void instance::bind() + { + // Register some global states + if (m_debugger) + { + _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); + m_debugger = nullptr; + } + + enable_debugging(); + } + + std::vector& instance::enumerate_devices() + { + u32 num_gpus; + // This may fail on unsupported drivers, so just assume no devices + if (vkEnumeratePhysicalDevices(m_instance, &num_gpus, nullptr) != VK_SUCCESS) + return gpus; + + if (gpus.size() != num_gpus) + { + std::vector pdevs(num_gpus); + gpus.resize(num_gpus); + + CHECK_RESULT(vkEnumeratePhysicalDevices(m_instance, &num_gpus, pdevs.data())); + + for (u32 i = 0; i < num_gpus; ++i) + gpus[i].create(m_instance, pdevs[i], extensions_loaded); + } + + return gpus; + } + + swapchain_base* instance::create_swapchain(display_handle_t window_handle, vk::physical_device& dev) + { + WSI_config surface_config + { + .supports_automatic_wm_reports = true + }; + m_surface = make_WSI_surface(m_instance, window_handle, &surface_config); + + u32 device_queues = dev.get_queue_count(); + std::vector supports_present(device_queues, VK_FALSE); + bool present_possible = true; + + for (u32 index = 0; index < device_queues; index++) + { + vkGetPhysicalDeviceSurfaceSupportKHR(dev, index, m_surface, &supports_present[index]); + } + + u32 graphics_queue_idx = -1; + u32 present_queue_idx = -1; + u32 transfer_queue_idx = -1; + + auto test_queue_family = [&](u32 index, u32 desired_flags) + { + if (const auto flags = dev.get_queue_properties(index).queueFlags; + (flags & desired_flags) == desired_flags) + { + return true; + } + + return false; + }; + + for (u32 i = 0; i < device_queues; ++i) + { + // 1. Test for a present queue possibly one that also supports present + if (present_queue_idx == umax && supports_present[i]) + { + present_queue_idx = i; + if (test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) + { + graphics_queue_idx = i; + } + } + // 2. Check for graphics support + else if (graphics_queue_idx == umax && test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) + { + graphics_queue_idx = i; + if (supports_present[i]) + { + present_queue_idx = i; + } + } + // 3. Check if transfer + compute is available + else if (transfer_queue_idx == umax && test_queue_family(i, VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) + { + transfer_queue_idx = i; + } + } + + if (graphics_queue_idx == umax) + { + rsx_log.fatal("Failed to find a suitable graphics queue"); + return nullptr; + } + + if (graphics_queue_idx != present_queue_idx) + { + // Separate graphics and present, use headless fallback + present_possible = false; + } + + if (!present_possible) + { + //Native(sw) swapchain + rsx_log.error("It is not possible for the currently selected GPU to present to the window (Likely caused by NVIDIA driver running the current display)"); + rsx_log.warning("Falling back to software present support (native windowing API)"); + auto swapchain = new swapchain_NATIVE(dev, -1, graphics_queue_idx, transfer_queue_idx); + swapchain->create(window_handle); + return swapchain; + } + + // Get the list of VkFormat's that are supported: + u32 formatCount; + CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, nullptr)); + + std::vector surfFormats(formatCount); + CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, surfFormats.data())); + + VkFormat format; + VkColorSpaceKHR color_space; + + if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) + { + format = VK_FORMAT_B8G8R8A8_UNORM; + } + else + { + if (!formatCount) fmt::throw_exception("Format count is zero!"); + format = surfFormats[0].format; + + //Prefer BGRA8_UNORM to avoid sRGB compression (RADV) + for (auto& surface_format : surfFormats) + { + if (surface_format.format == VK_FORMAT_B8G8R8A8_UNORM) + { + format = VK_FORMAT_B8G8R8A8_UNORM; + break; + } + } + } + + color_space = surfFormats[0].colorSpace; + + return new swapchain_WSI(dev, present_queue_idx, graphics_queue_idx, transfer_queue_idx, format, m_surface, color_space, !surface_config.supports_automatic_wm_reports); + } +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/instance.h b/rpcs3/Emu/RSX/VK/vkutils/instance.h new file mode 100644 index 0000000000..b4b87b1a76 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/instance.h @@ -0,0 +1,64 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "swapchain.h" + +#include +#include + +#ifdef __APPLE__ +#include +#include +#endif + +namespace vk +{ + class supported_extensions + { + private: + std::vector m_vk_exts; + + public: + enum enumeration_class + { + instance = 0, + device = 1 + }; + + supported_extensions(enumeration_class _class, const char* layer_name = nullptr, VkPhysicalDevice pdev = VK_NULL_HANDLE); + + bool is_supported(std::string_view ext) const; + }; + + class instance + { + private: + std::vector gpus; + VkInstance m_instance = VK_NULL_HANDLE; + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + + PFN_vkDestroyDebugReportCallbackEXT _vkDestroyDebugReportCallback = nullptr; + PFN_vkCreateDebugReportCallbackEXT _vkCreateDebugReportCallback = nullptr; + VkDebugReportCallbackEXT m_debugger = nullptr; + + bool extensions_loaded = false; + + public: + + instance() = default; + + ~instance(); + + void destroy(); + + void enable_debugging(); + + bool create(const char* app_name, bool fast = false); + + void bind(); + + std::vector& enumerate_devices(); + + swapchain_base* create_swapchain(display_handle_t window_handle, vk::physical_device& dev); + }; +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/instance.hpp b/rpcs3/Emu/RSX/VK/vkutils/instance.hpp deleted file mode 100644 index 19ae72f9e0..0000000000 --- a/rpcs3/Emu/RSX/VK/vkutils/instance.hpp +++ /dev/null @@ -1,457 +0,0 @@ -#pragma once - -#include "../VulkanAPI.h" -#include "swapchain.hpp" - -#include -#include - -#ifdef __APPLE__ -#include -#include -#endif - -namespace vk -{ - class supported_extensions - { - private: - std::vector m_vk_exts; - - public: - enum enumeration_class - { - instance = 0, - device = 1 - }; - - supported_extensions(enumeration_class _class, const char* layer_name = nullptr, VkPhysicalDevice pdev = VK_NULL_HANDLE) - { - u32 count; - if (_class == enumeration_class::instance) - { - if (vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr) != VK_SUCCESS) - return; - } - else - { - ensure(pdev); - if (vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, nullptr) != VK_SUCCESS) - return; - } - - m_vk_exts.resize(count); - if (_class == enumeration_class::instance) - { - vkEnumerateInstanceExtensionProperties(layer_name, &count, m_vk_exts.data()); - } - else - { - vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, m_vk_exts.data()); - } - } - - bool is_supported(std::string_view ext) - { - return std::any_of(m_vk_exts.cbegin(), m_vk_exts.cend(), [&](const VkExtensionProperties& p) { return p.extensionName == ext; }); - } - }; - - class instance - { - private: - std::vector gpus; - VkInstance m_instance = VK_NULL_HANDLE; - VkSurfaceKHR m_surface = VK_NULL_HANDLE; - - PFN_vkDestroyDebugReportCallbackEXT _vkDestroyDebugReportCallback = nullptr; - PFN_vkCreateDebugReportCallbackEXT _vkCreateDebugReportCallback = nullptr; - VkDebugReportCallbackEXT m_debugger = nullptr; - - bool extensions_loaded = false; - - public: - - instance() = default; - - ~instance() - { - if (m_instance) - { - destroy(); - } - } - - void destroy() - { - if (!m_instance) return; - - if (m_debugger) - { - _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); - m_debugger = nullptr; - } - - if (m_surface) - { - vkDestroySurfaceKHR(m_instance, m_surface, nullptr); - m_surface = VK_NULL_HANDLE; - } - - vkDestroyInstance(m_instance, nullptr); - m_instance = VK_NULL_HANDLE; - } - - void enable_debugging() - { - if (!g_cfg.video.debug_output) return; - - PFN_vkDebugReportCallbackEXT callback = vk::dbgFunc; - - _vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT")); - _vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkDestroyDebugReportCallbackEXT")); - - VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {}; - dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - dbgCreateInfo.pfnCallback = callback; - dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - - CHECK_RESULT(_vkCreateDebugReportCallback(m_instance, &dbgCreateInfo, NULL, &m_debugger)); - } -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -#endif - bool create(const char* app_name, bool fast = false) - { - // Initialize a vulkan instance - VkApplicationInfo app = {}; - - app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app.pApplicationName = app_name; - app.applicationVersion = 0; - app.pEngineName = app_name; - app.engineVersion = 0; - app.apiVersion = VK_API_VERSION_1_0; - - // Set up instance information - - std::vector extensions; - std::vector layers; - const void* next_info = nullptr; - -#ifdef __APPLE__ - // Declare MVK variables here to ensure the lifetime within the entire scope - const VkBool32 setting_true = VK_TRUE; - const int32_t setting_fast_math = g_cfg.video.disable_msl_fast_math.get() ? MVK_CONFIG_FAST_MATH_NEVER : MVK_CONFIG_FAST_MATH_ON_DEMAND; - - std::vector mvk_settings; - VkLayerSettingsCreateInfoEXT mvk_layer_settings_create_info{}; -#endif - - if (!fast) - { - extensions_loaded = true; - supported_extensions support(supported_extensions::instance); - - extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); - if (support.is_supported(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) - { - extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - } - - if (support.is_supported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); - } - -#ifdef __APPLE__ - if (support.is_supported(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME)) - { - extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME); - layers.push_back(kMVKMoltenVKDriverLayerName); - - mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_RESUME_LOST_DEVICE", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &setting_true }); - mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_FAST_MATH_ENABLED", VK_LAYER_SETTING_TYPE_INT32_EXT, 1, &setting_fast_math }); - - mvk_layer_settings_create_info.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT; - mvk_layer_settings_create_info.pNext = next_info; - mvk_layer_settings_create_info.settingCount = static_cast(mvk_settings.size()); - mvk_layer_settings_create_info.pSettings = mvk_settings.data(); - - next_info = &mvk_layer_settings_create_info; - } -#endif - - if (support.is_supported(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); - } - - if (support.is_supported(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); - } - - if (g_cfg.video.renderdoc_compatiblity && support.is_supported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) - { - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - -#ifdef _WIN32 - extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); -#elif defined(__APPLE__) - extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); -#else - bool found_surface_ext = false; -#ifdef HAVE_X11 - if (support.is_supported(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - found_surface_ext = true; - } -#endif -#ifdef VK_USE_PLATFORM_WAYLAND_KHR - if (support.is_supported(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); - found_surface_ext = true; - } -#endif //(WAYLAND) - if (!found_surface_ext) - { - rsx_log.error("Could not find a supported Vulkan surface extension"); - return 0; - } -#endif //(WIN32, __APPLE__) - if (g_cfg.video.debug_output) - layers.push_back("VK_LAYER_KHRONOS_validation"); - } - - VkInstanceCreateInfo instance_info = {}; - instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instance_info.pApplicationInfo = &app; - instance_info.enabledLayerCount = static_cast(layers.size()); - instance_info.ppEnabledLayerNames = layers.data(); - instance_info.enabledExtensionCount = fast ? 0 : static_cast(extensions.size()); - instance_info.ppEnabledExtensionNames = fast ? nullptr : extensions.data(); - instance_info.pNext = next_info; - - if (VkResult result = vkCreateInstance(&instance_info, nullptr, &m_instance); result != VK_SUCCESS) - { - if (result == VK_ERROR_LAYER_NOT_PRESENT) - { - rsx_log.fatal("Could not initialize layer VK_LAYER_KHRONOS_validation"); - } - - return false; - } - - return true; - } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - void bind() - { - // Register some global states - if (m_debugger) - { - _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); - m_debugger = nullptr; - } - - enable_debugging(); - } - - std::vector& enumerate_devices() - { - u32 num_gpus; - // This may fail on unsupported drivers, so just assume no devices - if (vkEnumeratePhysicalDevices(m_instance, &num_gpus, nullptr) != VK_SUCCESS) - return gpus; - - if (gpus.size() != num_gpus) - { - std::vector pdevs(num_gpus); - gpus.resize(num_gpus); - - CHECK_RESULT(vkEnumeratePhysicalDevices(m_instance, &num_gpus, pdevs.data())); - - for (u32 i = 0; i < num_gpus; ++i) - gpus[i].create(m_instance, pdevs[i], extensions_loaded); - } - - return gpus; - } - - swapchain_base* create_swapchain(display_handle_t window_handle, vk::physical_device& dev) - { - bool force_wm_reporting_off = false; -#ifdef _WIN32 - using swapchain_NATIVE = swapchain_WIN32; - HINSTANCE hInstance = NULL; - - VkWin32SurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - createInfo.hinstance = hInstance; - createInfo.hwnd = window_handle; - - CHECK_RESULT(vkCreateWin32SurfaceKHR(m_instance, &createInfo, NULL, &m_surface)); - -#elif defined(__APPLE__) - using swapchain_NATIVE = swapchain_MacOS; - VkMacOSSurfaceCreateInfoMVK createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - createInfo.pView = window_handle; - - CHECK_RESULT(vkCreateMacOSSurfaceMVK(m_instance, &createInfo, NULL, &m_surface)); -#else -#ifdef HAVE_X11 - using swapchain_NATIVE = swapchain_X11; -#else - using swapchain_NATIVE = swapchain_Wayland; -#endif - - std::visit([&](auto&& p) - { - using T = std::decay_t; - -#ifdef HAVE_X11 - if constexpr (std::is_same_v>) - { - VkXlibSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - createInfo.dpy = p.first; - createInfo.window = p.second; - CHECK_RESULT(vkCreateXlibSurfaceKHR(this->m_instance, &createInfo, nullptr, &m_surface)); - } - else -#endif -#ifdef HAVE_WAYLAND - if constexpr (std::is_same_v>) - { - VkWaylandSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - createInfo.display = p.first; - createInfo.surface = p.second; - CHECK_RESULT(vkCreateWaylandSurfaceKHR(this->m_instance, &createInfo, nullptr, &m_surface)); - force_wm_reporting_off = true; - } - else -#endif - { - static_assert(std::conditional_t::value, "Unhandled window_handle type in std::variant"); - } - }, window_handle); -#endif - - u32 device_queues = dev.get_queue_count(); - std::vector supports_present(device_queues, VK_FALSE); - bool present_possible = true; - - for (u32 index = 0; index < device_queues; index++) - { - vkGetPhysicalDeviceSurfaceSupportKHR(dev, index, m_surface, &supports_present[index]); - } - - u32 graphics_queue_idx = -1; - u32 present_queue_idx = -1; - u32 transfer_queue_idx = -1; - - auto test_queue_family = [&](u32 index, u32 desired_flags) - { - if (const auto flags = dev.get_queue_properties(index).queueFlags; - (flags & desired_flags) == desired_flags) - { - return true; - } - - return false; - }; - - for (u32 i = 0; i < device_queues; ++i) - { - // 1. Test for a present queue possibly one that also supports present - if (present_queue_idx == umax && supports_present[i]) - { - present_queue_idx = i; - if (test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) - { - graphics_queue_idx = i; - } - } - // 2. Check for graphics support - else if (graphics_queue_idx == umax && test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) - { - graphics_queue_idx = i; - if (supports_present[i]) - { - present_queue_idx = i; - } - } - // 3. Check if transfer + compute is available - else if (transfer_queue_idx == umax && test_queue_family(i, VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) - { - transfer_queue_idx = i; - } - } - - if (graphics_queue_idx == umax) - { - rsx_log.fatal("Failed to find a suitable graphics queue"); - return nullptr; - } - - if (graphics_queue_idx != present_queue_idx) - { - // Separate graphics and present, use headless fallback - present_possible = false; - } - - if (!present_possible) - { - //Native(sw) swapchain - rsx_log.error("It is not possible for the currently selected GPU to present to the window (Likely caused by NVIDIA driver running the current display)"); - rsx_log.warning("Falling back to software present support (native windowing API)"); - auto swapchain = new swapchain_NATIVE(dev, -1, graphics_queue_idx, transfer_queue_idx); - swapchain->create(window_handle); - return swapchain; - } - - // Get the list of VkFormat's that are supported: - u32 formatCount; - CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, nullptr)); - - std::vector surfFormats(formatCount); - CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, surfFormats.data())); - - VkFormat format; - VkColorSpaceKHR color_space; - - if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) - { - format = VK_FORMAT_B8G8R8A8_UNORM; - } - else - { - if (!formatCount) fmt::throw_exception("Format count is zero!"); - format = surfFormats[0].format; - - //Prefer BGRA8_UNORM to avoid sRGB compression (RADV) - for (auto& surface_format : surfFormats) - { - if (surface_format.format == VK_FORMAT_B8G8R8A8_UNORM) - { - format = VK_FORMAT_B8G8R8A8_UNORM; - break; - } - } - } - - color_space = surfFormats[0].colorSpace; - - return new swapchain_WSI(dev, present_queue_idx, graphics_queue_idx, transfer_queue_idx, format, m_surface, color_space, force_wm_reporting_off); - } - }; -} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp new file mode 100644 index 0000000000..a296e393e2 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp @@ -0,0 +1,354 @@ +#include "stdafx.h" +#include "swapchain.h" + +namespace vk +{ + // Swapchain image RPCS3 + swapchain_image_RPCS3::swapchain_image_RPCS3(render_device& dev, const memory_type_mapping& memory_map, u32 width, u32 height) + :image(dev, memory_map.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, width, height, 1, 1, 1, + VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0, VMM_ALLOCATION_POOL_SWAPCHAIN) + { + m_width = width; + m_height = height; + current_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + m_dma_buffer = std::make_unique(dev, m_width * m_height * 4, memory_map.host_visible_coherent, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0, VMM_ALLOCATION_POOL_SWAPCHAIN); + } + + void swapchain_image_RPCS3::do_dma_transfer(command_buffer& cmd) + { + VkBufferImageCopy copyRegion = {}; + copyRegion.bufferOffset = 0; + copyRegion.bufferRowLength = m_width; + copyRegion.bufferImageHeight = m_height; + copyRegion.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; + copyRegion.imageOffset = {}; + copyRegion.imageExtent = { m_width, m_height, 1 }; + + change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + vkCmdCopyImageToBuffer(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_dma_buffer->value, 1, ©Region); + change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + } + + u32 swapchain_image_RPCS3::get_required_memory_size() const + { + return m_width * m_height * 4; + } + + void* swapchain_image_RPCS3::get_pixels() + { + return m_dma_buffer->map(0, VK_WHOLE_SIZE); + } + + void swapchain_image_RPCS3::free_pixels() + { + m_dma_buffer->unmap(); + } + + // swapchain BASE + swapchain_base::swapchain_base(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format) + { + dev.create(gpu, graphics_queue, present_queue, transfer_queue); + m_surface_format = format; + } + + // NATIVE swapchain base + VkResult native_swapchain_base::acquire_next_swapchain_image(VkSemaphore /*semaphore*/, u64 /*timeout*/, u32* result) + { + u32 index = 0; + for (auto& p : swapchain_images) + { + if (!p.first) + { + p.first = true; + *result = index; + return VK_SUCCESS; + } + + ++index; + } + + return VK_NOT_READY; + } + + void native_swapchain_base::init_swapchain_images(render_device& dev, u32 preferred_count) + { + swapchain_images.resize(preferred_count); + for (auto& img : swapchain_images) + { + img.second = std::make_unique(dev, dev.get_memory_mapping(), m_width, m_height); + img.first = false; + } + } + + // WSI implementation + void swapchain_WSI::init_swapchain_images(render_device& dev, u32 /*preferred_count*/) + { + u32 nb_swap_images = 0; + _vkGetSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); + + if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain"); + + std::vector vk_images; + vk_images.resize(nb_swap_images); + _vkGetSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, vk_images.data()); + + swapchain_images.resize(nb_swap_images); + for (u32 i = 0; i < nb_swap_images; ++i) + { + swapchain_images[i].value = vk_images[i]; + } + } + + swapchain_WSI::swapchain_WSI(vk::physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space, bool force_wm_reporting_off) + : WSI_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + { + _vkCreateSwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); + _vkDestroySwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); + _vkGetSwapchainImagesKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); + _vkAcquireNextImageKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); + _vkQueuePresentKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkQueuePresentKHR")); + + m_surface = surface; + m_color_space = color_space; + + if (!force_wm_reporting_off) + { + switch (gpu.get_driver_vendor()) + { + case driver_vendor::AMD: + case driver_vendor::INTEL: + case driver_vendor::RADV: + case driver_vendor::MVK: + break; + case driver_vendor::ANV: + case driver_vendor::NVIDIA: + m_wm_reports_flag = true; + break; + default: + break; + } + } + } + + void swapchain_WSI::destroy(bool) + { + if (VkDevice pdev = dev) + { + if (m_vk_swapchain) + { + _vkDestroySwapchainKHR(pdev, m_vk_swapchain, nullptr); + } + + dev.destroy(); + } + } + + std::pair swapchain_WSI::init_surface_capabilities() + { +#ifdef _WIN32 + if (g_cfg.video.vk.exclusive_fullscreen_mode != vk_exclusive_fs_mode::unspecified && dev.get_surface_capabilities_2_support()) + { + HMONITOR hmonitor = MonitorFromWindow(window_handle, MONITOR_DEFAULTTOPRIMARY); + if (hmonitor) + { + VkSurfaceCapabilities2KHR pSurfaceCapabilities = {}; + pSurfaceCapabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; + + VkPhysicalDeviceSurfaceInfo2KHR pSurfaceInfo = {}; + pSurfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; + pSurfaceInfo.surface = m_surface; + + VkSurfaceCapabilitiesFullScreenExclusiveEXT full_screen_exclusive_capabilities = {}; + VkSurfaceFullScreenExclusiveWin32InfoEXT full_screen_exclusive_win32_info = {}; + full_screen_exclusive_capabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT; + + pSurfaceCapabilities.pNext = &full_screen_exclusive_capabilities; + + full_screen_exclusive_win32_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT; + full_screen_exclusive_win32_info.hmonitor = hmonitor; + + pSurfaceInfo.pNext = &full_screen_exclusive_win32_info; + + auto getPhysicalDeviceSurfaceCapabilities2KHR = reinterpret_cast( + vkGetInstanceProcAddr(dev.gpu(), "vkGetPhysicalDeviceSurfaceCapabilities2KHR") + ); + ensure(getPhysicalDeviceSurfaceCapabilities2KHR); + CHECK_RESULT(getPhysicalDeviceSurfaceCapabilities2KHR(dev.gpu(), &pSurfaceInfo, &pSurfaceCapabilities)); + + return { pSurfaceCapabilities.surfaceCapabilities, !!full_screen_exclusive_capabilities.fullScreenExclusiveSupported }; + } + else + { + rsx_log.warning("Swapchain: failed to get monitor for the window"); + } + } +#endif + VkSurfaceCapabilitiesKHR surface_descriptors = {}; + CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev.gpu(), m_surface, &surface_descriptors)); + return { surface_descriptors, false }; + } + + bool swapchain_WSI::init() + { + if (dev.get_present_queue() == VK_NULL_HANDLE) + { + rsx_log.error("Cannot create WSI swapchain without a present queue"); + return false; + } + + VkSwapchainKHR old_swapchain = m_vk_swapchain; + vk::physical_device& gpu = const_cast(dev.gpu()); + + auto [surface_descriptors, should_specify_exclusive_full_screen_mode] = init_surface_capabilities(); + + if (surface_descriptors.maxImageExtent.width < m_width || + surface_descriptors.maxImageExtent.height < m_height) + { + rsx_log.error("Swapchain: Swapchain creation failed because dimensions cannot fit. Max = %d, %d, Requested = %d, %d", + surface_descriptors.maxImageExtent.width, surface_descriptors.maxImageExtent.height, m_width, m_height); + + return false; + } + + if (surface_descriptors.currentExtent.width != umax) + { + if (surface_descriptors.currentExtent.width == 0 || surface_descriptors.currentExtent.height == 0) + { + rsx_log.warning("Swapchain: Current surface extent is a null region. Is the window minimized?"); + return false; + } + + m_width = surface_descriptors.currentExtent.width; + m_height = surface_descriptors.currentExtent.height; + } + + u32 nb_available_modes = 0; + CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, nullptr)); + + std::vector present_modes(nb_available_modes); + CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, present_modes.data())); + + VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR; + std::vector preferred_modes; + + if (!g_cfg.video.vk.force_fifo) + { + // List of preferred modes in decreasing desirability + // NOTE: Always picks "triple-buffered vsync" types if possible + if (!g_cfg.video.vsync) + { + preferred_modes = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR }; + } + } + + bool mode_found = false; + for (VkPresentModeKHR preferred_mode : preferred_modes) + { + //Search for this mode in supported modes + for (VkPresentModeKHR mode : present_modes) + { + if (mode == preferred_mode) + { + swapchain_present_mode = mode; + mode_found = true; + break; + } + } + + if (mode_found) + break; + } + + rsx_log.notice("Swapchain: present mode %d in use.", static_cast(swapchain_present_mode)); + + u32 nb_swap_images = surface_descriptors.minImageCount + 1; + if (surface_descriptors.maxImageCount > 0) + { + //Try to negotiate for a triple buffer setup + //In cases where the front-buffer isnt available for present, its better to have a spare surface + nb_swap_images = std::max(surface_descriptors.minImageCount + 2u, 3u); + + if (nb_swap_images > surface_descriptors.maxImageCount) + { + // Application must settle for fewer images than desired: + nb_swap_images = surface_descriptors.maxImageCount; + } + } + + VkSurfaceTransformFlagBitsKHR pre_transform = surface_descriptors.currentTransform; + if (surface_descriptors.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + + VkSwapchainCreateInfoKHR swap_info = {}; + swap_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swap_info.surface = m_surface; + swap_info.minImageCount = nb_swap_images; + swap_info.imageFormat = m_surface_format; + swap_info.imageColorSpace = m_color_space; + + swap_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swap_info.preTransform = pre_transform; + swap_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swap_info.imageArrayLayers = 1; + swap_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swap_info.presentMode = swapchain_present_mode; + swap_info.oldSwapchain = old_swapchain; + swap_info.clipped = true; + + swap_info.imageExtent.width = std::max(m_width, surface_descriptors.minImageExtent.width); + swap_info.imageExtent.height = std::max(m_height, surface_descriptors.minImageExtent.height); + +#ifdef _WIN32 + VkSurfaceFullScreenExclusiveInfoEXT full_screen_exclusive_info = {}; + if (should_specify_exclusive_full_screen_mode) + { + vk_exclusive_fs_mode fs_mode = g_cfg.video.vk.exclusive_fullscreen_mode; + ensure(fs_mode == vk_exclusive_fs_mode::enable || fs_mode == vk_exclusive_fs_mode::disable); + + full_screen_exclusive_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT; + full_screen_exclusive_info.fullScreenExclusive = + fs_mode == vk_exclusive_fs_mode::enable ? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT : VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT; + + swap_info.pNext = &full_screen_exclusive_info; + } + + rsx_log.notice("Swapchain: requesting full screen exclusive mode %d.", static_cast(full_screen_exclusive_info.fullScreenExclusive)); +#endif + + _vkCreateSwapchainKHR(dev, &swap_info, nullptr, &m_vk_swapchain); + + if (old_swapchain) + { + if (!swapchain_images.empty()) + { + swapchain_images.clear(); + } + + _vkDestroySwapchainKHR(dev, old_swapchain, nullptr); + } + + init_swapchain_images(dev); + return true; + } + + VkResult swapchain_WSI::present(VkSemaphore semaphore, u32 image) + { + VkPresentInfoKHR present = {}; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.pNext = nullptr; + present.swapchainCount = 1; + present.pSwapchains = &m_vk_swapchain; + present.pImageIndices = ℑ + + if (semaphore != VK_NULL_HANDLE) + { + present.waitSemaphoreCount = 1; + present.pWaitSemaphores = &semaphore; + } + + return _vkQueuePresentKHR(dev.get_present_queue(), &present); + } +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.h b/rpcs3/Emu/RSX/VK/vkutils/swapchain.h new file mode 100644 index 0000000000..f5128fc487 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined (_WIN32) +#include "swapchain_win32.hpp" +#elif defined (ANDROID) +#include "swapchain_android.hpp" +#elif defined (__APPLE__) +#include "swapchain_macos.hpp" +#else // Both linux and BSD families +#include "swapchain_unix.hpp" +#endif diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain.hpp deleted file mode 100644 index bca489d8d8..0000000000 --- a/rpcs3/Emu/RSX/VK/vkutils/swapchain.hpp +++ /dev/null @@ -1,786 +0,0 @@ -#pragma once - -#ifdef HAVE_X11 -#include -#endif - -#include "../../display.h" -#include "../VulkanAPI.h" -#include "image.h" - -#include - -namespace vk -{ - struct swapchain_image_WSI - { - VkImage value = VK_NULL_HANDLE; - }; - - class swapchain_image_RPCS3 : public image - { - std::unique_ptr m_dma_buffer; - u32 m_width = 0; - u32 m_height = 0; - - public: - swapchain_image_RPCS3(render_device& dev, const memory_type_mapping& memory_map, u32 width, u32 height) - :image(dev, memory_map.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, width, height, 1, 1, 1, - VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0, VMM_ALLOCATION_POOL_SWAPCHAIN) - { - m_width = width; - m_height = height; - current_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - m_dma_buffer = std::make_unique(dev, m_width * m_height * 4, memory_map.host_visible_coherent, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0, VMM_ALLOCATION_POOL_SWAPCHAIN); - } - - void do_dma_transfer(command_buffer& cmd) - { - VkBufferImageCopy copyRegion = {}; - copyRegion.bufferOffset = 0; - copyRegion.bufferRowLength = m_width; - copyRegion.bufferImageHeight = m_height; - copyRegion.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; - copyRegion.imageOffset = {}; - copyRegion.imageExtent = { m_width, m_height, 1 }; - - change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - vkCmdCopyImageToBuffer(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_dma_buffer->value, 1, ©Region); - change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - } - - u32 get_required_memory_size() const - { - return m_width * m_height * 4; - } - - void* get_pixels() - { - return m_dma_buffer->map(0, VK_WHOLE_SIZE); - } - - void free_pixels() - { - m_dma_buffer->unmap(); - } - }; - - class swapchain_base - { - protected: - render_device dev; - - display_handle_t window_handle{}; - u32 m_width = 0; - u32 m_height = 0; - VkFormat m_surface_format = VK_FORMAT_B8G8R8A8_UNORM; - - virtual void init_swapchain_images(render_device& dev, u32 count) = 0; - - public: - swapchain_base(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - { - dev.create(gpu, graphics_queue, present_queue, transfer_queue); - m_surface_format = format; - } - - virtual ~swapchain_base() = default; - - virtual void create(display_handle_t& handle) = 0; - virtual void destroy(bool full = true) = 0; - virtual bool init() = 0; - - virtual u32 get_swap_image_count() const = 0; - virtual VkImage get_image(u32 index) = 0; - virtual VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) = 0; - virtual void end_frame(command_buffer& cmd, u32 index) = 0; - virtual VkResult present(VkSemaphore semaphore, u32 index) = 0; - virtual VkImageLayout get_optimal_present_layout() = 0; - - virtual bool supports_automatic_wm_reports() const - { - return false; - } - - bool init(u32 w, u32 h) - { - m_width = w; - m_height = h; - return init(); - } - - const vk::render_device& get_device() - { - return dev; - } - - VkFormat get_surface_format() - { - return m_surface_format; - } - - bool is_headless() const - { - return (dev.get_present_queue() == VK_NULL_HANDLE); - } - }; - - template - class abstract_swapchain_impl : public swapchain_base - { - protected: - std::vector swapchain_images; - - public: - abstract_swapchain_impl(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~abstract_swapchain_impl() override = default; - - u32 get_swap_image_count() const override - { - return ::size32(swapchain_images); - } - - using swapchain_base::init; - }; - - using native_swapchain_base = abstract_swapchain_impl>>; - using WSI_swapchain_base = abstract_swapchain_impl; - - #ifdef _WIN32 - - class swapchain_WIN32 : public native_swapchain_base - { - HDC hDstDC = NULL; - HDC hSrcDC = NULL; - HBITMAP hDIB = NULL; - LPVOID hPtr = NULL; - - public: - swapchain_WIN32(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_WIN32() {} - - bool init() override - { - if (hDIB || hSrcDC) - destroy(false); - - RECT rect; - GetClientRect(window_handle, &rect); - m_width = rect.right - rect.left; - m_height = rect.bottom - rect.top; - - if (m_width == 0 || m_height == 0) - { - rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); - return false; - } - - BITMAPINFO bitmap = {}; - bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmap.bmiHeader.biWidth = m_width; - bitmap.bmiHeader.biHeight = m_height * -1; - bitmap.bmiHeader.biPlanes = 1; - bitmap.bmiHeader.biBitCount = 32; - bitmap.bmiHeader.biCompression = BI_RGB; - - hSrcDC = CreateCompatibleDC(hDstDC); - hDIB = CreateDIBSection(hSrcDC, &bitmap, DIB_RGB_COLORS, &hPtr, NULL, 0); - SelectObject(hSrcDC, hDIB); - init_swapchain_images(dev, 3); - return true; - } - - void create(display_handle_t& handle) override - { - window_handle = handle; - hDstDC = GetDC(handle); - } - - void destroy(bool full = true) override - { - DeleteObject(hDIB); - DeleteDC(hSrcDC); - hDIB = NULL; - hSrcDC = NULL; - - swapchain_images.clear(); - - if (full) - { - ReleaseDC(window_handle, hDstDC); - hDstDC = NULL; - - dev.destroy(); - } - } - - VkResult present(VkSemaphore /*semaphore*/, u32 image) override - { - auto& src = swapchain_images[image]; - GdiFlush(); - - if (hSrcDC) - { - memcpy(hPtr, src.second->get_pixels(), src.second->get_required_memory_size()); - BitBlt(hDstDC, 0, 0, m_width, m_height, hSrcDC, 0, 0, SRCCOPY); - src.second->free_pixels(); - } - - src.first = false; - return VK_SUCCESS; - } - #elif defined(__APPLE__) - - class swapchain_MacOS : public native_swapchain_base - { - void* nsView = nullptr; - - public: - swapchain_MacOS(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_MacOS() {} - - bool init() override - { - //TODO: get from `nsView` - m_width = 0; - m_height = 0; - - if (m_width == 0 || m_height == 0) - { - rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); - return false; - } - - init_swapchain_images(dev, 3); - return true; - } - - void create(display_handle_t& window_handle) override - { - nsView = window_handle; - } - - void destroy(bool full = true) override - { - swapchain_images.clear(); - - if (full) - dev.destroy(); - } - - VkResult present(VkSemaphore /*semaphore*/, u32 /*index*/) override - { - fmt::throw_exception("Native macOS swapchain is not implemented yet!"); - } - #elif defined(HAVE_X11) - - class swapchain_X11 : public native_swapchain_base - { - Display* display = nullptr; - Window window = 0; - XImage* pixmap = nullptr; - GC gc = nullptr; - int bit_depth = 24; - - public: - swapchain_X11(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_X11() override = default; - - bool init() override - { - if (pixmap) - destroy(false); - - Window root; - int x, y; - u32 w = 0, h = 0, border, depth; - - if (XGetGeometry(display, window, &root, &x, &y, &w, &h, &border, &depth)) - { - m_width = w; - m_height = h; - bit_depth = depth; - } - - if (m_width == 0 || m_height == 0) - { - rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); - return false; - } - - XVisualInfo visual{}; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wold-style-cast" - if (!XMatchVisualInfo(display, DefaultScreen(display), bit_depth, TrueColor, &visual)) - #pragma GCC diagnostic pop - { - rsx_log.error("Could not find matching visual info!"); - return false; - } - - pixmap = XCreateImage(display, visual.visual, visual.depth, ZPixmap, 0, nullptr, m_width, m_height, 32, 0); - init_swapchain_images(dev, 3); - return true; - } - - void create(display_handle_t& window_handle) override - { - std::visit([&](auto&& p) - { - using T = std::decay_t; - if constexpr (std::is_same_v>) - { - display = p.first; - window = p.second; - } - }, window_handle); - - if (display == NULL) - { - rsx_log.fatal("Could not create virtual display on this window protocol (Wayland?)"); - return; - } - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wold-style-cast" - gc = DefaultGC(display, DefaultScreen(display)); - #pragma GCC diagnostic pop - } - - void destroy(bool full = true) override - { - pixmap->data = nullptr; - XDestroyImage(pixmap); - pixmap = NULL; - - swapchain_images.clear(); - - if (full) - dev.destroy(); - } - - VkResult present(VkSemaphore /*semaphore*/, u32 index) override - { - auto& src = swapchain_images[index]; - if (pixmap) - { - pixmap->data = static_cast(src.second->get_pixels()); - - XPutImage(display, window, gc, pixmap, 0, 0, 0, 0, m_width, m_height); - XFlush(display); - - src.second->free_pixels(); - } - - //Release reference - src.first = false; - return VK_SUCCESS; - } - #else - - class swapchain_Wayland : public native_swapchain_base - { - - public: - swapchain_Wayland(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_Wayland() {} - - bool init() override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - - void create(display_handle_t& window_handle) override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - - void destroy(bool full = true) override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - - VkResult present(VkSemaphore /*semaphore*/, u32 index) override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - #endif - - VkResult acquire_next_swapchain_image(VkSemaphore /*semaphore*/, u64 /*timeout*/, u32* result) override - { - u32 index = 0; - for (auto& p : swapchain_images) - { - if (!p.first) - { - p.first = true; - *result = index; - return VK_SUCCESS; - } - - ++index; - } - - return VK_NOT_READY; - } - - void end_frame(command_buffer& cmd, u32 index) override - { - swapchain_images[index].second->do_dma_transfer(cmd); - } - - VkImage get_image(u32 index) override - { - return swapchain_images[index].second->value; - } - - VkImageLayout get_optimal_present_layout() override - { - return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - } - - protected: - void init_swapchain_images(render_device& dev, u32 preferred_count) override - { - swapchain_images.resize(preferred_count); - for (auto& img : swapchain_images) - { - img.second = std::make_unique(dev, dev.get_memory_mapping(), m_width, m_height); - img.first = false; - } - } - }; - - class swapchain_WSI : public WSI_swapchain_base - { - VkSurfaceKHR m_surface = VK_NULL_HANDLE; - VkColorSpaceKHR m_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; - VkSwapchainKHR m_vk_swapchain = nullptr; - - PFN_vkCreateSwapchainKHR _vkCreateSwapchainKHR = nullptr; - PFN_vkDestroySwapchainKHR _vkDestroySwapchainKHR = nullptr; - PFN_vkGetSwapchainImagesKHR _vkGetSwapchainImagesKHR = nullptr; - PFN_vkAcquireNextImageKHR _vkAcquireNextImageKHR = nullptr; - PFN_vkQueuePresentKHR _vkQueuePresentKHR = nullptr; - - bool m_wm_reports_flag = false; - - protected: - void init_swapchain_images(render_device& dev, u32 /*preferred_count*/ = 0) override - { - u32 nb_swap_images = 0; - _vkGetSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); - - if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain"); - - std::vector vk_images; - vk_images.resize(nb_swap_images); - _vkGetSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, vk_images.data()); - - swapchain_images.resize(nb_swap_images); - for (u32 i = 0; i < nb_swap_images; ++i) - { - swapchain_images[i].value = vk_images[i]; - } - } - - public: - swapchain_WSI(vk::physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space, bool force_wm_reporting_off) - : WSI_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - { - _vkCreateSwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); - _vkDestroySwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); - _vkGetSwapchainImagesKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); - _vkAcquireNextImageKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); - _vkQueuePresentKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkQueuePresentKHR")); - - m_surface = surface; - m_color_space = color_space; - - if (!force_wm_reporting_off) - { - switch (gpu.get_driver_vendor()) - { - case driver_vendor::AMD: - case driver_vendor::INTEL: - case driver_vendor::RADV: - case driver_vendor::MVK: - break; - case driver_vendor::ANV: - case driver_vendor::NVIDIA: - m_wm_reports_flag = true; - break; - default: - break; - } - } - } - - ~swapchain_WSI() override = default; - - void create(display_handle_t&) override - {} - - void destroy(bool = true) override - { - if (VkDevice pdev = dev) - { - if (m_vk_swapchain) - { - _vkDestroySwapchainKHR(pdev, m_vk_swapchain, nullptr); - } - - dev.destroy(); - } - } - - std::pair init_surface_capabilities() - { -#ifdef _WIN32 - if (g_cfg.video.vk.exclusive_fullscreen_mode != vk_exclusive_fs_mode::unspecified && dev.get_surface_capabilities_2_support()) - { - HMONITOR hmonitor = MonitorFromWindow(window_handle, MONITOR_DEFAULTTOPRIMARY); - if (hmonitor) - { - VkSurfaceCapabilities2KHR pSurfaceCapabilities = {}; - pSurfaceCapabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; - - VkPhysicalDeviceSurfaceInfo2KHR pSurfaceInfo = {}; - pSurfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; - pSurfaceInfo.surface = m_surface; - - VkSurfaceCapabilitiesFullScreenExclusiveEXT full_screen_exclusive_capabilities = {}; - VkSurfaceFullScreenExclusiveWin32InfoEXT full_screen_exclusive_win32_info = {}; - full_screen_exclusive_capabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT; - - pSurfaceCapabilities.pNext = &full_screen_exclusive_capabilities; - - full_screen_exclusive_win32_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT; - full_screen_exclusive_win32_info.hmonitor = hmonitor; - - pSurfaceInfo.pNext = &full_screen_exclusive_win32_info; - - auto getPhysicalDeviceSurfaceCapabilities2KHR = reinterpret_cast( - vkGetInstanceProcAddr(dev.gpu(), "vkGetPhysicalDeviceSurfaceCapabilities2KHR") - ); - ensure(getPhysicalDeviceSurfaceCapabilities2KHR); - CHECK_RESULT(getPhysicalDeviceSurfaceCapabilities2KHR(dev.gpu(), &pSurfaceInfo, &pSurfaceCapabilities)); - - return { pSurfaceCapabilities.surfaceCapabilities, !!full_screen_exclusive_capabilities.fullScreenExclusiveSupported }; - } - else - { - rsx_log.warning("Swapchain: failed to get monitor for the window"); - } - } -#endif - VkSurfaceCapabilitiesKHR surface_descriptors = {}; - CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev.gpu(), m_surface, &surface_descriptors)); - return { surface_descriptors, false }; - } - - using WSI_swapchain_base::init; - bool init() override - { - if (dev.get_present_queue() == VK_NULL_HANDLE) - { - rsx_log.error("Cannot create WSI swapchain without a present queue"); - return false; - } - - VkSwapchainKHR old_swapchain = m_vk_swapchain; - vk::physical_device& gpu = const_cast(dev.gpu()); - - auto [surface_descriptors, should_specify_exclusive_full_screen_mode] = init_surface_capabilities(); - - if (surface_descriptors.maxImageExtent.width < m_width || - surface_descriptors.maxImageExtent.height < m_height) - { - rsx_log.error("Swapchain: Swapchain creation failed because dimensions cannot fit. Max = %d, %d, Requested = %d, %d", - surface_descriptors.maxImageExtent.width, surface_descriptors.maxImageExtent.height, m_width, m_height); - - return false; - } - - if (surface_descriptors.currentExtent.width != umax) - { - if (surface_descriptors.currentExtent.width == 0 || surface_descriptors.currentExtent.height == 0) - { - rsx_log.warning("Swapchain: Current surface extent is a null region. Is the window minimized?"); - return false; - } - - m_width = surface_descriptors.currentExtent.width; - m_height = surface_descriptors.currentExtent.height; - } - - u32 nb_available_modes = 0; - CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, nullptr)); - - std::vector present_modes(nb_available_modes); - CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, present_modes.data())); - - VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR; - std::vector preferred_modes; - - if (!g_cfg.video.vk.force_fifo) - { - // List of preferred modes in decreasing desirability - // NOTE: Always picks "triple-buffered vsync" types if possible - if (!g_cfg.video.vsync) - { - preferred_modes = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR }; - } - } - - bool mode_found = false; - for (VkPresentModeKHR preferred_mode : preferred_modes) - { - //Search for this mode in supported modes - for (VkPresentModeKHR mode : present_modes) - { - if (mode == preferred_mode) - { - swapchain_present_mode = mode; - mode_found = true; - break; - } - } - - if (mode_found) - break; - } - - rsx_log.notice("Swapchain: present mode %d in use.", static_cast(swapchain_present_mode)); - - u32 nb_swap_images = surface_descriptors.minImageCount + 1; - if (surface_descriptors.maxImageCount > 0) - { - //Try to negotiate for a triple buffer setup - //In cases where the front-buffer isnt available for present, its better to have a spare surface - nb_swap_images = std::max(surface_descriptors.minImageCount + 2u, 3u); - - if (nb_swap_images > surface_descriptors.maxImageCount) - { - // Application must settle for fewer images than desired: - nb_swap_images = surface_descriptors.maxImageCount; - } - } - - VkSurfaceTransformFlagBitsKHR pre_transform = surface_descriptors.currentTransform; - if (surface_descriptors.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) - pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - - VkSwapchainCreateInfoKHR swap_info = {}; - swap_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swap_info.surface = m_surface; - swap_info.minImageCount = nb_swap_images; - swap_info.imageFormat = m_surface_format; - swap_info.imageColorSpace = m_color_space; - - swap_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - swap_info.preTransform = pre_transform; - swap_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swap_info.imageArrayLayers = 1; - swap_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swap_info.presentMode = swapchain_present_mode; - swap_info.oldSwapchain = old_swapchain; - swap_info.clipped = true; - - swap_info.imageExtent.width = std::max(m_width, surface_descriptors.minImageExtent.width); - swap_info.imageExtent.height = std::max(m_height, surface_descriptors.minImageExtent.height); - - #ifdef _WIN32 - VkSurfaceFullScreenExclusiveInfoEXT full_screen_exclusive_info = {}; - if (should_specify_exclusive_full_screen_mode) - { - vk_exclusive_fs_mode fs_mode = g_cfg.video.vk.exclusive_fullscreen_mode; - ensure(fs_mode == vk_exclusive_fs_mode::enable || fs_mode == vk_exclusive_fs_mode::disable); - - full_screen_exclusive_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT; - full_screen_exclusive_info.fullScreenExclusive = - fs_mode == vk_exclusive_fs_mode::enable ? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT : VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT; - - swap_info.pNext = &full_screen_exclusive_info; - } - - rsx_log.notice("Swapchain: requesting full screen exclusive mode %d.", static_cast(full_screen_exclusive_info.fullScreenExclusive)); - #endif - - _vkCreateSwapchainKHR(dev, &swap_info, nullptr, &m_vk_swapchain); - - if (old_swapchain) - { - if (!swapchain_images.empty()) - { - swapchain_images.clear(); - } - - _vkDestroySwapchainKHR(dev, old_swapchain, nullptr); - } - - init_swapchain_images(dev); - return true; - } - - bool supports_automatic_wm_reports() const override - { - return m_wm_reports_flag; - } - - VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) override - { - return vkAcquireNextImageKHR(dev, m_vk_swapchain, timeout, semaphore, VK_NULL_HANDLE, result); - } - - void end_frame(command_buffer& /*cmd*/, u32 /*index*/) override - { - } - - VkResult present(VkSemaphore semaphore, u32 image) override - { - VkPresentInfoKHR present = {}; - present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present.pNext = nullptr; - present.swapchainCount = 1; - present.pSwapchains = &m_vk_swapchain; - present.pImageIndices = ℑ - - if (semaphore != VK_NULL_HANDLE) - { - present.waitSemaphoreCount = 1; - present.pWaitSemaphores = &semaphore; - } - - return _vkQueuePresentKHR(dev.get_present_queue(), &present); - } - - VkImage get_image(u32 index) override - { - return swapchain_images[index].value; - } - - VkImageLayout get_optimal_present_layout() override - { - return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - } - }; -} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp new file mode 100644 index 0000000000..ccec22a383 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(ANDROID) + using swapchain_ANDROID = native_swapchain_base; + using swapchain_NATIVE = swapchain_ANDROID; + + // TODO: Implement this + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* /*config*/) + { + return VK_NULL_HANDLE; + } +#endif +} + diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h b/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h new file mode 100644 index 0000000000..aaf8426b9a --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h @@ -0,0 +1,227 @@ +#pragma once + +#ifdef HAVE_X11 +#include +#endif + +#include "../../display.h" +#include "../VulkanAPI.h" +#include "image.h" + +#include + +namespace vk +{ + struct swapchain_image_WSI + { + VkImage value = VK_NULL_HANDLE; + }; + + class swapchain_image_RPCS3 : public image + { + std::unique_ptr m_dma_buffer; + u32 m_width = 0; + u32 m_height = 0; + + public: + swapchain_image_RPCS3(render_device& dev, const memory_type_mapping& memory_map, u32 width, u32 height); + + void do_dma_transfer(command_buffer& cmd); + + u32 get_required_memory_size() const; + + void* get_pixels(); + + void free_pixels(); + }; + + class swapchain_base + { + protected: + render_device dev; + + display_handle_t window_handle{}; + u32 m_width = 0; + u32 m_height = 0; + VkFormat m_surface_format = VK_FORMAT_B8G8R8A8_UNORM; + + virtual void init_swapchain_images(render_device& dev, u32 count) = 0; + + public: + swapchain_base(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM); + + virtual ~swapchain_base() = default; + + virtual void create(display_handle_t& handle) = 0; + virtual void destroy(bool full = true) = 0; + virtual bool init() = 0; + + virtual u32 get_swap_image_count() const = 0; + virtual VkImage get_image(u32 index) = 0; + virtual VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) = 0; + virtual void end_frame(command_buffer& cmd, u32 index) = 0; + virtual VkResult present(VkSemaphore semaphore, u32 index) = 0; + virtual VkImageLayout get_optimal_present_layout() const = 0; + + virtual bool supports_automatic_wm_reports() const + { + return false; + } + + bool init(u32 w, u32 h) + { + m_width = w; + m_height = h; + return init(); + } + + const vk::render_device& get_device() + { + return dev; + } + + VkFormat get_surface_format() const + { + return m_surface_format; + } + + bool is_headless() const + { + return (dev.get_present_queue() == VK_NULL_HANDLE); + } + }; + + template + class abstract_swapchain_impl : public swapchain_base + { + protected: + std::vector swapchain_images; + + public: + abstract_swapchain_impl(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~abstract_swapchain_impl() override = default; + + u32 get_swap_image_count() const override + { + return ::size32(swapchain_images); + } + + using swapchain_base::init; + }; + + using WSI_swapchain_base = abstract_swapchain_impl; + + class native_swapchain_base : public abstract_swapchain_impl>> + { + public: + using abstract_swapchain_impl::abstract_swapchain_impl; + + VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) override; + + // Clients must implement these methods to render without WSI support + bool init() override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + void create(display_handle_t& /*window_handle*/) override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + void destroy(bool /*full*/ = true) override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 /*index*/) override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + // Generic accessors + void end_frame(command_buffer& cmd, u32 index) override + { + swapchain_images[index].second->do_dma_transfer(cmd); + } + + VkImage get_image(u32 index) override + { + return swapchain_images[index].second->value; + } + + VkImageLayout get_optimal_present_layout() const override + { + return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + + protected: + void init_swapchain_images(render_device& dev, u32 preferred_count) override; + }; + + class swapchain_WSI : public WSI_swapchain_base + { + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + VkColorSpaceKHR m_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + VkSwapchainKHR m_vk_swapchain = nullptr; + + PFN_vkCreateSwapchainKHR _vkCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR _vkDestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR _vkGetSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR _vkAcquireNextImageKHR = nullptr; + PFN_vkQueuePresentKHR _vkQueuePresentKHR = nullptr; + + bool m_wm_reports_flag = false; + + protected: + void init_swapchain_images(render_device& dev, u32 preferred_count = 0) override; + + public: + swapchain_WSI(vk::physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space, bool force_wm_reporting_off); + + ~swapchain_WSI() override = default; + + void create(display_handle_t&) override + {} + + void destroy(bool = true) override; + + std::pair init_surface_capabilities(); + + using WSI_swapchain_base::init; + bool init() override; + + bool supports_automatic_wm_reports() const override + { + return m_wm_reports_flag; + } + + VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) override + { + return vkAcquireNextImageKHR(dev, m_vk_swapchain, timeout, semaphore, VK_NULL_HANDLE, result); + } + + void end_frame(command_buffer& /*cmd*/, u32 /*index*/) override + {} + + VkResult present(VkSemaphore semaphore, u32 image) override; + + VkImage get_image(u32 index) override + { + return swapchain_images[index].value; + } + + VkImageLayout get_optimal_present_layout() const override + { + return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + }; + + struct WSI_config + { + bool supports_automatic_wm_reports = true; + }; +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp new file mode 100644 index 0000000000..9e4217692d --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(__APPLE__) + using swapchain_MacOS = native_swapchain_base; + using swapchain_NATIVE = swapchain_MacOS; + + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* /*config*/) + { + VkSurfaceKHR result = VK_NULL_HANDLE; + VkMacOSSurfaceCreateInfoMVK createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + createInfo.pView = window_handle; + + CHECK_RESULT(vkCreateMacOSSurfaceMVK(vk_instance, &createInfo, NULL, &result)); + return result; + } +#endif +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp new file mode 100644 index 0000000000..ec893da8e8 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp @@ -0,0 +1,172 @@ +#pragma once + +#include "swapchain_core.h" + +#ifdef HAVE_X11 +#include +#endif + +namespace vk +{ +#if defined(HAVE_X11) + + class swapchain_X11 : public native_swapchain_base + { + Display* display = nullptr; + Window window = 0; + XImage* pixmap = nullptr; + GC gc = nullptr; + int bit_depth = 24; + + public: + swapchain_X11(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~swapchain_X11() override = default; + + bool init() override + { + if (pixmap) + destroy(false); + + Window root; + int x, y; + u32 w = 0, h = 0, border, depth; + + if (XGetGeometry(display, window, &root, &x, &y, &w, &h, &border, &depth)) + { + m_width = w; + m_height = h; + bit_depth = depth; + } + + if (m_width == 0 || m_height == 0) + { + rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + XVisualInfo visual{}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + if (!XMatchVisualInfo(display, DefaultScreen(display), bit_depth, TrueColor, &visual)) +#pragma GCC diagnostic pop + { + rsx_log.error("Could not find matching visual info!"); + return false; + } + + pixmap = XCreateImage(display, visual.visual, visual.depth, ZPixmap, 0, nullptr, m_width, m_height, 32, 0); + init_swapchain_images(dev, 3); + return true; + } + + void create(display_handle_t& window_handle) override + { + std::visit([&](auto&& p) + { + using T = std::decay_t; + if constexpr (std::is_same_v>) + { + display = p.first; + window = p.second; + } + }, window_handle); + + if (display == NULL) + { + rsx_log.fatal("Could not create virtual display on this window protocol (Wayland?)"); + return; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + gc = DefaultGC(display, DefaultScreen(display)); +#pragma GCC diagnostic pop + } + + void destroy(bool full = true) override + { + pixmap->data = nullptr; + XDestroyImage(pixmap); + pixmap = NULL; + + swapchain_images.clear(); + + if (full) + dev.destroy(); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 index) override + { + auto& src = swapchain_images[index]; + if (pixmap) + { + pixmap->data = static_cast(src.second->get_pixels()); + + XPutImage(display, window, gc, pixmap, 0, 0, 0, 0, m_width, m_height); + XFlush(display); + + src.second->free_pixels(); + } + + //Release reference + src.first = false; + return VK_SUCCESS; + } + }; + + using swapchain_NATIVE = swapchain_X11; + +#endif + +#if defined(HAVE_WAYLAND) + using swapchain_Wayland = native_swapchain_base; + +#ifndef HAVE_X11 + using swapchain_NATIVE = swapchain_Wayland; +#endif + +#endif + + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* config) + { + VkSurfaceKHR result = VK_NULL_HANDLE; + + std::visit([&](auto&& p) + { + using T = std::decay_t; + +#ifdef HAVE_X11 + if constexpr (std::is_same_v>) + { + VkXlibSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + createInfo.dpy = p.first; + createInfo.window = p.second; + CHECK_RESULT(vkCreateXlibSurfaceKHR(vk_instance, &createInfo, nullptr, &result)); + } + else +#endif +#ifdef HAVE_WAYLAND + if constexpr (std::is_same_v>) + { + VkWaylandSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.display = p.first; + createInfo.surface = p.second; + CHECK_RESULT(vkCreateWaylandSurfaceKHR(vk_instance, &createInfo, nullptr, &result)); + config->supports_automatic_wm_reports = false; + } + else +#endif + { + static_assert(std::conditional_t::value, "Unhandled window_handle type in std::variant"); + } + }, window_handle); + + return ensure(result, "Failed to initialize Vulkan display surface"); + } +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp new file mode 100644 index 0000000000..c509d7cffa --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(_WIN32) + class swapchain_WIN32 : public native_swapchain_base + { + HDC hDstDC = NULL; + HDC hSrcDC = NULL; + HBITMAP hDIB = NULL; + LPVOID hPtr = NULL; + + public: + swapchain_WIN32(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~swapchain_WIN32() {} + + bool init() override + { + if (hDIB || hSrcDC) + destroy(false); + + RECT rect; + GetClientRect(window_handle, &rect); + m_width = rect.right - rect.left; + m_height = rect.bottom - rect.top; + + if (m_width == 0 || m_height == 0) + { + rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + BITMAPINFO bitmap = {}; + bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmap.bmiHeader.biWidth = m_width; + bitmap.bmiHeader.biHeight = m_height * -1; + bitmap.bmiHeader.biPlanes = 1; + bitmap.bmiHeader.biBitCount = 32; + bitmap.bmiHeader.biCompression = BI_RGB; + + hSrcDC = CreateCompatibleDC(hDstDC); + hDIB = CreateDIBSection(hSrcDC, &bitmap, DIB_RGB_COLORS, &hPtr, NULL, 0); + SelectObject(hSrcDC, hDIB); + init_swapchain_images(dev, 3); + return true; + } + + void create(display_handle_t& handle) override + { + window_handle = handle; + hDstDC = GetDC(handle); + } + + void destroy(bool full = true) override + { + DeleteObject(hDIB); + DeleteDC(hSrcDC); + hDIB = NULL; + hSrcDC = NULL; + + swapchain_images.clear(); + + if (full) + { + ReleaseDC(window_handle, hDstDC); + hDstDC = NULL; + + dev.destroy(); + } + } + + VkResult present(VkSemaphore /*semaphore*/, u32 image) override + { + auto& src = swapchain_images[image]; + GdiFlush(); + + if (hSrcDC) + { + memcpy(hPtr, src.second->get_pixels(), src.second->get_required_memory_size()); + BitBlt(hDstDC, 0, 0, m_width, m_height, hSrcDC, 0, 0, SRCCOPY); + src.second->free_pixels(); + } + + src.first = false; + return VK_SUCCESS; + } + }; + + using swapchain_NATIVE = swapchain_WIN32; + + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* /*config*/) + { + HINSTANCE hInstance = NULL; + VkSurfaceKHR result = VK_NULL_HANDLE; + + VkWin32SurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + createInfo.hinstance = hInstance; + createInfo.hwnd = window_handle; + CHECK_RESULT(vkCreateWin32SurfaceKHR(vk_instance, &createInfo, NULL, &result)); + return result; + } +#endif +} diff --git a/rpcs3/Emu/RSX/rsx_utils.cpp b/rpcs3/Emu/RSX/rsx_utils.cpp index 7ca410a657..2a781a9c44 100644 --- a/rpcs3/Emu/RSX/rsx_utils.cpp +++ b/rpcs3/Emu/RSX/rsx_utils.cpp @@ -40,7 +40,7 @@ namespace rsx void clip_image(u8 *dst, const u8 *src, int clip_x, int clip_y, int clip_w, int clip_h, int bpp, int src_pitch, int dst_pitch) { const u8* pixels_src = src + clip_y * src_pitch + clip_x * bpp; - u8 *pixels_dst = dst; + u8* pixels_dst = dst; const u32 row_length = clip_w * bpp; for (int y = 0; y < clip_h; ++y) diff --git a/rpcs3/Emu/RSX/rsx_utils.h b/rpcs3/Emu/RSX/rsx_utils.h index c5ce6de16d..46ff619577 100644 --- a/rpcs3/Emu/RSX/rsx_utils.h +++ b/rpcs3/Emu/RSX/rsx_utils.h @@ -213,9 +213,9 @@ namespace rsx }; template - void pad_texture(void* input_pixels, void* output_pixels, u16 input_width, u16 input_height, u16 output_width, u16 /*output_height*/) + void pad_texture(const void* input_pixels, void* output_pixels, u16 input_width, u16 input_height, u16 output_width, u16 /*output_height*/) { - T *src = static_cast(input_pixels); + const T *src = static_cast(input_pixels); T *dst = static_cast(output_pixels); for (u16 h = 0; h < input_height; ++h) @@ -336,8 +336,8 @@ namespace rsx template void convert_linear_swizzle(const void* input_pixels, void* output_pixels, u16 width, u16 height, u32 pitch) { - u32 log2width = ceil_log2(width); - u32 log2height = ceil_log2(height); + const u32 log2width = ceil_log2(width); + const u32 log2height = ceil_log2(height); // Max mask possible for square texture u32 x_mask = 0x55555555; @@ -356,7 +356,7 @@ namespace rsx u32 offs_y = 0; u32 offs_x = 0; u32 offs_x0 = 0; //total y-carry offset for x - u32 y_incr = limit_mask; + const u32 y_incr = limit_mask; // NOTE: The swizzled area is always a POT region and we must scan all of it to fill in the linear. // It is assumed that there is no padding on the linear side for simplicity - backend upload/download will crop as needed. @@ -586,8 +586,8 @@ namespace rsx template static inline const std::pair apply_resolution_scale(u16 width, u16 height, u16 ref_width = 0, u16 ref_height = 0) { - ref_width = (ref_width)? ref_width : width; - ref_height = (ref_height)? ref_height : height; + ref_width = (ref_width) ? ref_width : width; + ref_height = (ref_height) ? ref_height : height; const u16 ref = std::max(ref_width, ref_height); if (ref > g_cfg.video.min_scalable_dimension) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 7cb2c444aa..0f03311a82 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -51,8 +51,6 @@ #include "Utilities/JIT.h" -#include "display_sleep_control.h" - #include "Emu/IPC_socket.h" #if defined(HAVE_VULKAN) @@ -90,7 +88,6 @@ extern std::pair, CellError> ppu_load_overlay(const ppu_ extern bool ppu_load_rel_exec(const ppu_rel_object&); extern void send_close_home_menu_cmds(); -extern void check_microphone_permissions(); extern void signal_system_cache_can_stay(); @@ -1750,7 +1747,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, fs::file src{path}; - src = decrypt_self(std::move(src)); + src = decrypt_self(src); const ppu_exec_object obj = src; @@ -1767,6 +1764,8 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, g_fxo->init("SPRX Loader"sv, [this, dir_queue]() mutable { + std::vector*> mod_list; + if (auto& _main = *ensure(g_fxo->try_get>()); !_main.path.empty()) { if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, std::vector{}, [](){ return Emu.IsStopped(); })) @@ -1776,6 +1775,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, Emu.ConfigurePPUCache(); ppu_initialize(_main); + mod_list.emplace_back(&_main); } if (Emu.IsStopped()) @@ -1783,7 +1783,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, return; } - ppu_precompile(dir_queue, nullptr); + ppu_precompile(dir_queue, mod_list.empty() ? nullptr : &mod_list); if (Emu.IsStopped()) { @@ -1810,7 +1810,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, { if (const std::vector device_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"}); !device_list.empty()) { - check_microphone_permissions(); + Emu.GetCallbacks().check_microphone_permissions(); } } @@ -2241,7 +2241,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, { // 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); + elf_file = decrypt_self(elf_file, klic.empty() ? nullptr : reinterpret_cast(&klic[0]), &g_ps3_process_info.self_info); } else { @@ -2507,7 +2507,7 @@ void Emulator::Run(bool start_playtime) if (g_cfg.misc.prevent_display_sleep) { - disable_display_sleep(); + Emu.GetCallbacks().enable_display_sleep(false); } } @@ -2792,7 +2792,7 @@ bool Emulator::Pause(bool freeze_emulation, bool show_resume_message) } // Always Enable display sleep, not only if it was prevented. - enable_display_sleep(); + Emu.GetCallbacks().enable_display_sleep(true); return true; } @@ -2889,7 +2889,7 @@ void Emulator::Resume() if (g_cfg.misc.prevent_display_sleep) { - disable_display_sleep(); + Emu.GetCallbacks().enable_display_sleep(false); } } @@ -3799,7 +3799,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s GetCallbacks().on_stop(); // Always Enable display sleep, not only if it was prevented. - enable_display_sleep(); + Emu.GetCallbacks().enable_display_sleep(true); if (allow_autoexit) { diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 83a889af94..b74e78ff10 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -107,6 +107,9 @@ struct EmuCallbacks std::function()> get_font_dirs; std::function&)> on_install_pkgs; std::function add_breakpoint; + std::function display_sleep_control_supported; + std::function enable_display_sleep; + std::function check_microphone_permissions; }; namespace utils diff --git a/rpcs3/Input/xinput_pad_handler.cpp b/rpcs3/Input/xinput_pad_handler.cpp index 49c2467faf..f23cb7fd79 100644 --- a/rpcs3/Input/xinput_pad_handler.cpp +++ b/rpcs3/Input/xinput_pad_handler.cpp @@ -3,6 +3,7 @@ #include "stdafx.h" #include "xinput_pad_handler.h" #include "Emu/Io/pad_config.h" +#include "util/dyn_lib.hpp" namespace XINPUT_INFO { @@ -77,8 +78,6 @@ xinput_pad_handler::~xinput_pad_handler() { if (library) { - FreeLibrary(library); - library = nullptr; xinputGetExtended = nullptr; xinputGetCustomData = nullptr; xinputGetState = nullptr; @@ -374,12 +373,6 @@ pad_preview_values xinput_pad_handler::get_preview_values(const std::unordered_m }; } -template -T getProc(HMODULE hModule, LPCSTR lpProcName) -{ - return reinterpret_cast(GetProcAddress(hModule, lpProcName)); -} - bool xinput_pad_handler::Init() { if (m_is_init) @@ -387,17 +380,17 @@ bool xinput_pad_handler::Init() for (auto it : XINPUT_INFO::LIBRARY_FILENAMES) { - library = LoadLibrary(it); + library.load(it); if (library) { - xinputGetExtended = getProc(library, "XInputGetExtended"); // Optional - xinputGetCustomData = getProc(library, "XInputGetCustomData"); // Optional - xinputGetState = getProc(library, reinterpret_cast(100)); + xinputGetExtended = library.get("XInputGetExtended"); // Optional + xinputGetCustomData = library.get("XInputGetCustomData"); // Optional + xinputGetState = library.get(reinterpret_cast(100)); if (!xinputGetState) - xinputGetState = getProc(library, "XInputGetState"); + xinputGetState = library.get("XInputGetState"); - xinputSetState = getProc(library, "XInputSetState"); - xinputGetBatteryInformation = getProc(library, "XInputGetBatteryInformation"); + xinputSetState = library.get("XInputSetState"); + xinputGetBatteryInformation = library.get("XInputGetBatteryInformation"); if (xinputGetState && xinputSetState && xinputGetBatteryInformation) { @@ -405,8 +398,6 @@ bool xinput_pad_handler::Init() break; } - FreeLibrary(library); - library = nullptr; xinputGetExtended = nullptr; xinputGetCustomData = nullptr; xinputGetState = nullptr; diff --git a/rpcs3/Input/xinput_pad_handler.h b/rpcs3/Input/xinput_pad_handler.h index 2ca25bd336..899e649b13 100644 --- a/rpcs3/Input/xinput_pad_handler.h +++ b/rpcs3/Input/xinput_pad_handler.h @@ -1,6 +1,7 @@ #pragma once #include "Emu/Io/PadHandler.h" +#include "util/dyn_lib.hpp" #include @@ -9,7 +10,6 @@ #endif #include #include -#include // ScpToolkit defined structure for pressure sensitive button query struct SCP_EXTN @@ -116,22 +116,22 @@ public: void init_config(cfg_pad* cfg) override; private: - typedef DWORD (WINAPI * PFN_XINPUTGETEXTENDED)(DWORD, SCP_EXTN *); - typedef DWORD (WINAPI * PFN_XINPUTGETCUSTOMDATA)(DWORD, DWORD, void *); - typedef DWORD (WINAPI * PFN_XINPUTGETSTATE)(DWORD, XINPUT_STATE *); - typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *); - typedef DWORD (WINAPI * PFN_XINPUTGETBATTERYINFORMATION)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *); + using PFN_XINPUTGETEXTENDED = DWORD(WINAPI*)(DWORD, SCP_EXTN*); + using PFN_XINPUTGETCUSTOMDATA = DWORD(WINAPI*)(DWORD, DWORD, void*); + using PFN_XINPUTGETSTATE = DWORD(WINAPI*)(DWORD, XINPUT_STATE*); + using PFN_XINPUTSETSTATE = DWORD(WINAPI*)(DWORD, XINPUT_VIBRATION*); + using PFN_XINPUTGETBATTERYINFORMATION = DWORD(WINAPI*)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*); int GetDeviceNumber(const std::string& padId); static PadButtonValues get_button_values_base(const XINPUT_STATE& state, trigger_recognition_mode trigger_mode); static PadButtonValues get_button_values_scp(const SCP_EXTN& state, trigger_recognition_mode trigger_mode); - HMODULE library{ nullptr }; PFN_XINPUTGETEXTENDED xinputGetExtended{ nullptr }; PFN_XINPUTGETCUSTOMDATA xinputGetCustomData{ nullptr }; PFN_XINPUTGETSTATE xinputGetState{ nullptr }; PFN_XINPUTSETSTATE xinputSetState{ nullptr }; PFN_XINPUTGETBATTERYINFORMATION xinputGetBatteryInformation{ nullptr }; + utils::dynamic_library library; std::shared_ptr get_device(const std::string& device) override; bool get_is_left_trigger(const std::shared_ptr& device, u64 keyCode) override; diff --git a/rpcs3/VKGSRender.vcxproj b/rpcs3/VKGSRender.vcxproj index 868ff9ad66..fb5c517adc 100644 --- a/rpcs3/VKGSRender.vcxproj +++ b/rpcs3/VKGSRender.vcxproj @@ -49,7 +49,12 @@ - + + + + + + @@ -58,7 +63,7 @@ - + @@ -94,7 +99,9 @@ + + diff --git a/rpcs3/VKGSRender.vcxproj.filters b/rpcs3/VKGSRender.vcxproj.filters index 153a21a9b0..d9adc1939e 100644 --- a/rpcs3/VKGSRender.vcxproj.filters +++ b/rpcs3/VKGSRender.vcxproj.filters @@ -73,6 +73,12 @@ upscalers\fsr1 + + vkutils + + + vkutils + @@ -124,7 +130,7 @@ vkutils - + vkutils @@ -139,7 +145,7 @@ vkutils - + vkutils @@ -175,6 +181,21 @@ vkutils + + vkutils + + + vkutils + + + vkutils + + + vkutils + + + vkutils + diff --git a/rpcs3/display_sleep_control.cpp b/rpcs3/display_sleep_control.cpp index 42c371dfe8..27fefa8b3e 100644 --- a/rpcs3/display_sleep_control.cpp +++ b/rpcs3/display_sleep_control.cpp @@ -39,7 +39,7 @@ bool display_sleep_control_supported() #endif } -void enable_display_sleep() +void enable_display_sleep(bool enabled) { if (!display_sleep_control_supported()) { @@ -47,15 +47,23 @@ void enable_display_sleep() } #ifdef _WIN32 - SetThreadExecutionState(ES_CONTINUOUS); + SetThreadExecutionState(enabled ? ES_CONTINUOUS : (ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)); #elif defined(__APPLE__) - if (s_pm_assertion != kIOPMNullAssertionID) + if (enabled && s_pm_assertion != kIOPMNullAssertionID) { IOPMAssertionRelease(s_pm_assertion); s_pm_assertion = kIOPMNullAssertionID; } + else if (!enabled) + { +#pragma GCC diagnostic push +// Necessary as some of those values are macro using old casts +#pragma GCC diagnostic ignored "-Wold-style-cast" + IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Game running"), &s_pm_assertion); +#pragma GCC diagnostic pop + } #elif defined(HAVE_QTDBUS) - if (s_dbus_cookie != 0) + if (enabled && s_dbus_cookie != 0) { for (const char* service : { "org.freedesktop.ScreenSaver", "org.mate.ScreenSaver" }) { @@ -68,36 +76,20 @@ void enable_display_sleep() } s_dbus_cookie = 0; } -#endif -} - -void disable_display_sleep() -{ - if (!display_sleep_control_supported()) + else if (!enabled) { - return; - } - -#ifdef _WIN32 - SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); -#elif defined(__APPLE__) -#pragma GCC diagnostic push -// Necessary as some of those values are macro using old casts -#pragma GCC diagnostic ignored "-Wold-style-cast" - IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Game running"), &s_pm_assertion); -#pragma GCC diagnostic pop -#elif defined(HAVE_QTDBUS) - for (const char* service : { "org.freedesktop.ScreenSaver", "org.mate.ScreenSaver" }) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) + for (const char* service : { "org.freedesktop.ScreenSaver", "org.mate.ScreenSaver" }) { - QDBusReply reply = interface.call("Inhibit", "rpcs3", "Game running"); - if (reply.isValid()) + QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); + if (interface.isValid()) { - s_dbus_cookie = reply.value(); + QDBusReply reply = interface.call("Inhibit", "rpcs3", "Game running"); + if (reply.isValid()) + { + s_dbus_cookie = reply.value(); + } + break; } - break; } } #endif diff --git a/rpcs3/display_sleep_control.h b/rpcs3/display_sleep_control.h index e2c7ce2168..66d246a46b 100644 --- a/rpcs3/display_sleep_control.h +++ b/rpcs3/display_sleep_control.h @@ -1,5 +1,4 @@ #pragma once bool display_sleep_control_supported(); -void enable_display_sleep(); -void disable_display_sleep(); +void enable_display_sleep(bool enabled); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 9a916678ae..b9a3b59bf0 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -60,6 +60,7 @@ + @@ -111,9 +112,11 @@ + + @@ -178,6 +181,7 @@ + NotUsing diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index de42637d4c..d08488e266 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1342,6 +1342,18 @@ Emu\GPU\RSX\Program + + Utilities + + + Utilities + + + Emu\GPU\RSX\Game Window + + + Emu\GPU\RSX\Common + diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index 098b84532f..ee7cd42cee 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -168,6 +168,11 @@ void headless_application::InitializeCallbacks() callbacks.play_sound = [](const std::string&){}; callbacks.add_breakpoint = [](u32 /*addr*/){}; + callbacks.display_sleep_control_supported = [](){ return false; }; + callbacks.enable_display_sleep = [](bool /*enabled*/){}; + + callbacks.check_microphone_permissions = [](){}; + Emu.SetCallbacks(std::move(callbacks)); } diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 1f6a96dfec..05b3119b7e 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -511,13 +511,6 @@ void log_q_debug(QtMsgType type, const QMessageLogContext& context, const QStrin } } -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-%dT%H:%M:%S", dateTime); -} - void run_platform_sanity_checks() { #ifdef _WIN32 diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index d86d6523b2..65a8117b7f 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -73,14 +73,7 @@ void main_application::OnEmuSettingsChange() { if (Emu.IsRunning()) { - if (g_cfg.misc.prevent_display_sleep) - { - disable_display_sleep(); - } - else - { - enable_display_sleep(); - } + enable_display_sleep(!g_cfg.misc.prevent_display_sleep); } if (!Emu.IsStopped()) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 3e703c9af0..cbc0111029 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -757,6 +757,7 @@ + @@ -1462,6 +1463,7 @@ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\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" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 199495aaba..bc2110067b 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1161,6 +1161,9 @@ Io\Move + + Gui\utils + @@ -1364,6 +1367,9 @@ Io\Move + + Gui\utils + diff --git a/rpcs3/rpcs3_version.cpp b/rpcs3/rpcs3_version.cpp index cafa870e2a..57bac63f22 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, 34, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; + static constexpr utils::version version{ 0, 0, 35, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; return version; } diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 140b39644d..29531499a7 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -61,6 +61,7 @@ add_library(rpcs3_ui STATIC pad_settings_dialog.cpp patch_creator_dialog.cpp patch_manager_dialog.cpp + permissions.cpp persistent_settings.cpp pkg_install_dialog.cpp progress_dialog.cpp diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.cpp b/rpcs3/rpcs3qt/camera_settings_dialog.cpp index 4d4ab3c58e..0cc5843595 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/camera_settings_dialog.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "camera_settings_dialog.h" #include "ui_camera_settings_dialog.h" +#include "permissions.h" #include "Emu/Io/camera_config.h" #include @@ -8,10 +9,6 @@ #include #include -#if QT_CONFIG(permissions) -#include -#endif - LOG_CHANNEL(camera_log, "Camera"); template <> @@ -230,26 +227,12 @@ void camera_settings_dialog::handle_settings_change(int index) return; } -#if QT_CONFIG(permissions) - const QCameraPermission permission; - switch (qApp->checkPermission(permission)) + if (!gui::utils::check_camera_permission(this, + [this, index](){ handle_settings_change(index); }, + [this](){ QMessageBox::warning(this, tr("Camera permissions denied!"), tr("RPCS3 has no permissions to access cameras on this device.")); })) { - case Qt::PermissionStatus::Undetermined: - camera_log.notice("Requesting camera permission"); - qApp->requestPermission(permission, this, [this, index]() - { - handle_settings_change(index); - }); return; - case Qt::PermissionStatus::Denied: - camera_log.error("RPCS3 has no permissions to access cameras on this device."); - QMessageBox::warning(this, tr("Camera permissions denied!"), tr("RPCS3 has no permissions to access cameras on this device.")); - return; - case Qt::PermissionStatus::Granted: - camera_log.notice("Camera permission granted"); - break; } -#endif if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) { diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index cd7fe67804..8f1f1cb049 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -29,29 +29,6 @@ LOG_CHANNEL(log_cheat, "Cheat"); cheat_manager_dialog* cheat_manager_dialog::inst = nullptr; -template <> -void fmt_class_string::format(std::string& out, u64 arg) -{ - format_enum(out, arg, [](cheat_type value) - { - switch (value) - { - case cheat_type::unsigned_8_cheat: return "Unsigned 8 bits"; - case cheat_type::unsigned_16_cheat: return "Unsigned 16 bits"; - case cheat_type::unsigned_32_cheat: return "Unsigned 32 bits"; - case cheat_type::unsigned_64_cheat: return "Unsigned 64 bits"; - case cheat_type::signed_8_cheat: return "Signed 8 bits"; - case cheat_type::signed_16_cheat: return "Signed 16 bits"; - case cheat_type::signed_32_cheat: return "Signed 32 bits"; - case cheat_type::signed_64_cheat: return "Signed 64 bits"; - case cheat_type::float_32_cheat: return "Float 32 bits"; - case cheat_type::max: break; - } - - return unknown; - }); -} - YAML::Emitter& operator<<(YAML::Emitter& out, const cheat_info& rhs) { std::string type_formatted; diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 56b1ccb0a4..b62efcc716 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -44,41 +44,9 @@ extern atomic_t g_debugger_pause_all_threads_on_bp; extern const ppu_decoder g_ppu_itype; -extern bool is_using_interpreter(thread_class t_class) -{ - switch (t_class) - { - case thread_class::ppu: return g_cfg.core.ppu_decoder != ppu_decoder_type::llvm; - case thread_class::spu: return g_cfg.core.spu_decoder != spu_decoder_type::asmjit && g_cfg.core.spu_decoder != spu_decoder_type::llvm; - default: return true; - } -} +extern bool is_using_interpreter(thread_class t_class); -extern std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle) -{ - if (!handle) - { - switch (cpu->get_class()) - { - case thread_class::ppu: handle = idm::get_unlocked>(cpu->id); break; - case thread_class::spu: handle = idm::get_unlocked>(cpu->id); break; - default: break; - } - } - - std::shared_ptr result; - - switch (cpu->get_class()) - { - case thread_class::ppu: result = std::make_shared(cpu_disasm_mode::interpreter, vm::g_sudo_addr); break; - case thread_class::spu: result = std::make_shared(cpu_disasm_mode::interpreter, static_cast(cpu)->ls); break; - case thread_class::rsx: result = std::make_shared(cpu_disasm_mode::interpreter, vm::g_sudo_addr, 0, cpu); break; - default: return result; - } - - result->set_cpu_handle(std::move(handle)); - return result; -} +extern std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle); debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidget *parent) : custom_dock_widget(tr("Debugger [Press F1 for Help]"), parent) diff --git a/rpcs3/rpcs3qt/game_compatibility.cpp b/rpcs3/rpcs3qt/game_compatibility.cpp index a51e9102e2..6a698d9936 100644 --- a/rpcs3/rpcs3qt/game_compatibility.cpp +++ b/rpcs3/rpcs3qt/game_compatibility.cpp @@ -262,7 +262,7 @@ compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, gam return info; } - const psf::registry psf = reader.get_psf(); + const psf::registry& psf = reader.get_psf(); // TODO: localization of title and changelog const std::string title_key = "TITLE"; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index b24ce33fc3..b8371a19e4 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -53,20 +53,14 @@ extern atomic_t g_user_asked_for_recording; extern atomic_t g_user_asked_for_screenshot; extern atomic_t g_user_asked_for_frame_capture; extern atomic_t g_disable_frame_limit; +extern atomic_t g_game_window_focused; extern atomic_t g_recording_mode; -atomic_t g_game_window_focused = false; - namespace pad { extern atomic_t g_home_menu_requested; } -bool is_input_allowed() -{ - return g_game_window_focused || g_cfg.io.background_input_enabled; -} - gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen) : QWindow() , m_initial_geometry(geometry) diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index b0a5d53812..4bf4426fca 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -2,6 +2,7 @@ #include "gui_application.h" #include "qt_utils.h" +#include "permissions.h" #include "welcome_dialog.h" #include "main_window.h" #include "emu_settings.h" @@ -13,6 +14,7 @@ #include "qt_camera_handler.h" #include "qt_music_handler.h" #include "rpcs3_version.h" +#include "display_sleep_control.h" #ifdef WITH_DISCORD_RPC #include "_discord_utils.h" @@ -878,6 +880,17 @@ void gui_application::InitializeCallbacks() }); }; + callbacks.display_sleep_control_supported = [](){ return display_sleep_control_supported(); }; + callbacks.enable_display_sleep = [](bool enabled){ enable_display_sleep(enabled); }; + + callbacks.check_microphone_permissions = []() + { + Emu.BlockingCallFromMainThread([]() + { + gui::utils::check_microphone_permission(); + }); + }; + Emu.SetCallbacks(std::move(callbacks)); } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 734bdf04e4..eaa72807f3 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -86,11 +86,6 @@ #include #include -#if QT_CONFIG(permissions) -#include -#include -#endif - #ifdef _WIN32 #include "raw_mouse_settings_dialog.h" #endif @@ -168,32 +163,6 @@ extern void qt_events_aware_op(int repeat_duration_ms, std::function wra } } -extern void check_microphone_permissions() -{ -#if QT_CONFIG(permissions) - Emu.BlockingCallFromMainThread([]() - { - const QMicrophonePermission permission; - switch (qApp->checkPermission(permission)) - { - case Qt::PermissionStatus::Undetermined: - gui_log.notice("Requesting microphone permission"); - qApp->requestPermission(permission, []() - { - check_microphone_permissions(); - }); - break; - case Qt::PermissionStatus::Denied: - gui_log.error("RPCS3 has no permissions to access microphones on this device."); - break; - case Qt::PermissionStatus::Granted: - gui_log.notice("Microphone permission granted"); - break; - } - }); -#endif -} - main_window::main_window(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget *parent) : QMainWindow(parent) , ui(new Ui::main_window) @@ -580,13 +549,12 @@ void main_window::show_boot_error(game_boot_result status) } const QString link = tr("

For information on setting up the emulator and dumping your PS3 games, read the quickstart guide.").arg(gui::utils::get_link_style()); - QMessageBox* msg = new QMessageBox(); + QMessageBox* msg = new QMessageBox(this); msg->setWindowTitle(tr("Boot Failed")); msg->setIcon(QMessageBox::Critical); msg->setTextFormat(Qt::RichText); msg->setStandardButtons(QMessageBox::Ok); msg->setText(tr("Booting failed: %1 %2").arg(message).arg(link)); - msg->setParent(this); msg->setAttribute(Qt::WA_DeleteOnClose); msg->open(); } diff --git a/rpcs3/rpcs3qt/microphone_creator.cpp b/rpcs3/rpcs3qt/microphone_creator.cpp index daf9116eff..a0e63a5d51 100644 --- a/rpcs3/rpcs3qt/microphone_creator.cpp +++ b/rpcs3/rpcs3qt/microphone_creator.cpp @@ -30,6 +30,7 @@ void microphone_creator::refresh_list() { while (*devices != 0) { + cfg_log.notice("Found microphone: %s", devices); m_microphone_list.append(devices); devices += strlen(devices) + 1; } diff --git a/rpcs3/rpcs3qt/permissions.cpp b/rpcs3/rpcs3qt/permissions.cpp new file mode 100644 index 0000000000..03043792ff --- /dev/null +++ b/rpcs3/rpcs3qt/permissions.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "permissions.h" + +#include + +#if QT_CONFIG(permissions) +#include +#include +#endif + +LOG_CHANNEL(gui_log, "GUI"); +LOG_CHANNEL(camera_log, "Camera"); + +namespace gui +{ + namespace utils + { + void check_microphone_permission() + { +#if QT_CONFIG(permissions) + const QMicrophonePermission permission; + switch (qApp->checkPermission(permission)) + { + case Qt::PermissionStatus::Undetermined: + gui_log.notice("Requesting microphone permission"); + qApp->requestPermission(permission, []() + { + check_microphone_permission(); + }); + break; + case Qt::PermissionStatus::Denied: + gui_log.error("RPCS3 has no permissions to access microphones on this device."); + break; + case Qt::PermissionStatus::Granted: + gui_log.notice("Microphone permission granted"); + break; + } +#endif + } + + bool check_camera_permission(void* obj, std::function repeat_callback, std::function denied_callback) + { +#if QT_CONFIG(permissions) + const QCameraPermission permission; + switch (qApp->checkPermission(permission)) + { + case Qt::PermissionStatus::Undetermined: + camera_log.notice("Requesting camera permission"); + qApp->requestPermission(permission, static_cast(obj), [repeat_callback]() + { + if (repeat_callback) repeat_callback(); + }); + return false; + case Qt::PermissionStatus::Denied: + camera_log.error("RPCS3 has no permissions to access cameras on this device."); + if (denied_callback) denied_callback(); + return false; + case Qt::PermissionStatus::Granted: + camera_log.notice("Camera permission granted"); + break; + } +#endif + return true; + } + } +} diff --git a/rpcs3/rpcs3qt/permissions.h b/rpcs3/rpcs3qt/permissions.h new file mode 100644 index 0000000000..4f7f6b2610 --- /dev/null +++ b/rpcs3/rpcs3qt/permissions.h @@ -0,0 +1,10 @@ +#pragma once + +namespace gui +{ + namespace utils + { + void check_microphone_permission(); + bool check_camera_permission(void* obj, std::function repeat_callback, std::function denied_callback); + } +} diff --git a/rpcs3/rpcs3qt/qt_camera_handler.cpp b/rpcs3/rpcs3qt/qt_camera_handler.cpp index 4703cf12b6..5b0caeb642 100644 --- a/rpcs3/rpcs3qt/qt_camera_handler.cpp +++ b/rpcs3/rpcs3qt/qt_camera_handler.cpp @@ -1,16 +1,12 @@ #include "stdafx.h" #include "qt_camera_handler.h" +#include "permissions.h" #include "Emu/system_config.h" #include "Emu/System.h" #include "Emu/Io/camera_config.h" #include -#if QT_CONFIG(permissions) -#include -#include -#endif - LOG_CHANNEL(camera_log, "Camera"); qt_camera_handler::qt_camera_handler() : camera_handler_base() @@ -215,25 +211,10 @@ void qt_camera_handler::start_camera() return; } -#if QT_CONFIG(permissions) - const QCameraPermission permission; - switch (qApp->checkPermission(permission)) + if (!gui::utils::check_camera_permission(this, [this](){ start_camera(); }, nullptr)) { - case Qt::PermissionStatus::Undetermined: - camera_log.notice("Requesting camera permission"); - qApp->requestPermission(permission, this, [this]() - { - start_camera(); - }); return; - case Qt::PermissionStatus::Denied: - camera_log.error("RPCS3 has no permissions to access cameras on this device."); - return; - case Qt::PermissionStatus::Granted: - camera_log.notice("Camera permission granted"); - break; } -#endif // Start camera. We will start receiving frames now. set_expected_state(camera_handler_state::running); diff --git a/rpcs3/rpcs3qt/render_creator.cpp b/rpcs3/rpcs3qt/render_creator.cpp index b85336bf8b..ef425366b3 100644 --- a/rpcs3/rpcs3qt/render_creator.cpp +++ b/rpcs3/rpcs3qt/render_creator.cpp @@ -5,7 +5,7 @@ #include "Utilities/Thread.h" #if defined(HAVE_VULKAN) -#include "Emu/RSX/VK/vkutils/instance.hpp" +#include "Emu/RSX/VK/vkutils/instance.h" #endif #include diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 9dabd04cc9..85af632ff2 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -14,7 +14,6 @@ #include #include "gui_settings.h" -#include "display_sleep_control.h" #include "qt_utils.h" #include "uuid.h" #include "settings_dialog.h" @@ -1822,7 +1821,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->preventDisplaySleep, emu_settings_type::PreventDisplaySleep); SubscribeTooltip(ui->preventDisplaySleep, tooltips.settings.prevent_display_sleep); - ui->preventDisplaySleep->setEnabled(display_sleep_control_supported()); + ui->preventDisplaySleep->setEnabled(Emu.GetCallbacks().display_sleep_control_supported()); m_emu_settings->EnhanceCheckBox(ui->showTrophyPopups, emu_settings_type::ShowTrophyPopups); SubscribeTooltip(ui->showTrophyPopups, tooltips.settings.show_trophy_popups); diff --git a/rpcs3/rpcs3qt/shortcut_utils.cpp b/rpcs3/rpcs3qt/shortcut_utils.cpp index 6c16cdff04..98f72d2872 100644 --- a/rpcs3/rpcs3qt/shortcut_utils.cpp +++ b/rpcs3/rpcs3qt/shortcut_utils.cpp @@ -16,6 +16,7 @@ #include #include "Emu/system_utils.hpp" +#include #else #include #include @@ -142,19 +143,17 @@ namespace gui::utils return false; } - IShellLink* pShellLink = nullptr; - IPersistFile* pPersistFile = nullptr; + Microsoft::WRL::ComPtr pShellLink; + Microsoft::WRL::ComPtr pPersistFile; const auto cleanup = [&](bool return_value, const std::string& fail_reason) -> bool { if (!return_value) sys_log.error("Failed to create shortcut: %s", fail_reason); - if (pPersistFile) pPersistFile->Release(); - if (pShellLink) pShellLink->Release(); CoUninitialize(); return return_value; }; - res = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink)); + res = CoCreateInstance(__uuidof(ShellLink), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink)); if (FAILED(res)) return cleanup(false, "CoCreateInstance failed"); @@ -200,7 +199,7 @@ namespace gui::utils } // Use the IPersistFile object to save the shell link - res = pShellLink->QueryInterface(IID_PPV_ARGS(&pPersistFile)); + res = pShellLink.As(&pPersistFile); if (FAILED(res)) return cleanup(false, fmt::format("QueryInterface failed (%s)", str_error(res))); diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index bab63ccfc6..947aa4f54a 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -376,21 +376,24 @@ namespace utils } // Align to power of 2 - template >> + template + requires std::is_unsigned_v constexpr std::make_unsigned_t> align(T value, U align) { return static_cast>>((value + (align - 1)) & (T{0} - align)); } // General purpose aligned division, the result is rounded up not truncated - template >> + template + requires std::is_unsigned_v constexpr T aligned_div(T value, std::type_identity_t align) { return static_cast(value / align + T{!!(value % align)}); } // General purpose aligned division, the result is rounded to nearest - template >> + template + requires std::is_integral_v constexpr T rounded_div(T value, std::type_identity_t align) { if constexpr (std::is_unsigned_v) diff --git a/rpcs3/util/atomic.hpp b/rpcs3/util/atomic.hpp index 4c3ab8959c..593b7a51f1 100644 --- a/rpcs3/util/atomic.hpp +++ b/rpcs3/util/atomic.hpp @@ -173,7 +173,8 @@ namespace atomic_wait constexpr list& operator=(const list&) noexcept = default; - template ().wait(any_value))...>> + template + requires(requires(U& u) { u.wait(any_value); } && ...) constexpr list(U&... vars) : m_info{{&vars, 0}...} { @@ -190,7 +191,8 @@ namespace atomic_wait return *this; } - template ().wait(any_value))>> + template + requires(requires(T2& t2) { t2.wait(any_value); }) constexpr void set(T2& var, U value) { static_assert(Index < Max); @@ -229,7 +231,8 @@ namespace atomic_wait } }; - template ().wait(any_value))...>> + template + requires(requires(T& t) { t.wait(any_value); } && ...) list(T&... vars) -> list; } diff --git a/rpcs3/util/dyn_lib.cpp b/rpcs3/util/dyn_lib.cpp index d739a28c09..1177aff229 100644 --- a/rpcs3/util/dyn_lib.cpp +++ b/rpcs3/util/dyn_lib.cpp @@ -29,6 +29,14 @@ namespace utils return loaded(); } +#ifdef _WIN32 + bool dynamic_library::load(const std::wstring& path) + { + m_handle = LoadLibraryW(path.c_str()); + return loaded(); + } +#endif + void dynamic_library::close() { #ifdef _WIN32 @@ -39,12 +47,12 @@ namespace utils m_handle = nullptr; } - void* dynamic_library::get_impl(const std::string& name) const + void* dynamic_library::get_impl(const char* name) const { #ifdef _WIN32 - return reinterpret_cast(GetProcAddress(reinterpret_cast(m_handle), name.c_str())); + return reinterpret_cast(GetProcAddress(reinterpret_cast(m_handle), name)); #else - return dlsym(m_handle, name.c_str()); + return dlsym(m_handle, name); #endif } diff --git a/rpcs3/util/dyn_lib.hpp b/rpcs3/util/dyn_lib.hpp index dcd958a818..2968f8daed 100644 --- a/rpcs3/util/dyn_lib.hpp +++ b/rpcs3/util/dyn_lib.hpp @@ -31,26 +31,19 @@ namespace utils ~dynamic_library(); bool load(const std::string& path); +#ifdef _WIN32 + bool load(const std::wstring& path); +#endif void close(); private: - void* get_impl(const std::string& name) const; + void* get_impl(const char* name) const; public: template - Type* get(const std::string& name) const + Type get(const char* name) const { - Type* result; - *reinterpret_cast(&result) = get_impl(name); - return result; - } - - template - bool get(Type*& function, const std::string& name) const - { - *reinterpret_cast(&function) = get_impl(name); - - return function != nullptr; + return reinterpret_cast(get_impl(name)); } bool loaded() const; diff --git a/rpcs3/util/emu_utils.cpp b/rpcs3/util/emu_utils.cpp new file mode 100644 index 0000000000..02d4f9865e --- /dev/null +++ b/rpcs3/util/emu_utils.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" + +#include "Emu/IdManager.h" +#include "Emu/system_config.h" +#include "Emu/Cell/PPUDisAsm.h" +#include "Emu/Cell/SPUDisAsm.h" +#include "Emu/Cell/SPUThread.h" +#include "Emu/Cell/PPUThread.h" +#include "Emu/RSX/RSXDisAsm.h" +#include "Emu/Memory/vm.h" +#include "Utilities/Thread.h" + +bool is_using_interpreter(thread_class t_class) +{ + switch (t_class) + { + case thread_class::ppu: return g_cfg.core.ppu_decoder != ppu_decoder_type::llvm; + case thread_class::spu: return g_cfg.core.spu_decoder != spu_decoder_type::asmjit && g_cfg.core.spu_decoder != spu_decoder_type::llvm; + default: return true; + } +} + +std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle) +{ + if (!handle) + { + switch (cpu->get_class()) + { + case thread_class::ppu: handle = idm::get_unlocked>(cpu->id); break; + case thread_class::spu: handle = idm::get_unlocked>(cpu->id); break; + default: break; + } + } + + std::shared_ptr result; + + switch (cpu->get_class()) + { + case thread_class::ppu: result = std::make_shared(cpu_disasm_mode::interpreter, vm::g_sudo_addr); break; + case thread_class::spu: result = std::make_shared(cpu_disasm_mode::interpreter, static_cast(cpu)->ls); break; + case thread_class::rsx: result = std::make_shared(cpu_disasm_mode::interpreter, vm::g_sudo_addr, 0, cpu); break; + default: return result; + } + + result->set_cpu_handle(std::move(handle)); + return result; +} diff --git a/rpcs3/util/endian.hpp b/rpcs3/util/endian.hpp index c4748b34d5..169eeffbad 100644 --- a/rpcs3/util/endian.hpp +++ b/rpcs3/util/endian.hpp @@ -267,8 +267,9 @@ private: } } -public: - template ())> + public: + template + requires requires(const T2& t2) { +t2; } constexpr bool operator==(const T2& rhs) const noexcept { using R = std::common_type_t; diff --git a/rpcs3/util/fixed_typemap.hpp b/rpcs3/util/fixed_typemap.hpp index 85d04c57a1..65d161c333 100644 --- a/rpcs3/util/fixed_typemap.hpp +++ b/rpcs3/util/fixed_typemap.hpp @@ -143,7 +143,8 @@ namespace stx *std::launder(static_cast(ptr)) = state; } - template requires requires (T& a) { a.save(std::declval>()); } + template + requires requires(T& a, utils::serial& ar) { a.save(stx::exact_t(ar)); } static void call_save(void* ptr, utils::serial& ar) noexcept { std::launder(static_cast(ptr))->save(stx::exact_t(ar)); @@ -169,7 +170,7 @@ namespace stx r.thread_op = &call_thread_op; } - if constexpr (!!(requires (T& a) { a.save(std::declval>()); })) + if constexpr (!!(requires(T& a, utils::serial& ar) { a.save(stx::exact_t(ar)); })) { r.save = &call_save; } diff --git a/rpcs3/util/fnv_hash.hpp b/rpcs3/util/fnv_hash.hpp index dea3d8b480..1da02d02a5 100644 --- a/rpcs3/util/fnv_hash.hpp +++ b/rpcs3/util/fnv_hash.hpp @@ -14,7 +14,8 @@ namespace rpcs3 return static_cast(value); } - template >> + template + requires std::is_integral_v static inline usz hash64(usz hash_value, T data) { hash_value ^= data; diff --git a/rpcs3/util/serialization.hpp b/rpcs3/util/serialization.hpp index 5e139e05f8..41f6e560af 100644 --- a/rpcs3/util/serialization.hpp +++ b/rpcs3/util/serialization.hpp @@ -18,10 +18,7 @@ namespace utils }; template - concept Bitcopy = (std::is_arithmetic_v) || (std::is_enum_v) || Integral || requires () - { - std::enable_if_t>(); - }; + concept Bitcopy = (std::is_arithmetic_v) || (std::is_enum_v) || Integral || typename T::enable_bitcopy()(); template concept TupleAlike = (!FastRandomAccess) && requires () @@ -30,8 +27,8 @@ namespace utils }; template - concept ListAlike = requires (std::remove_cvref_t& obj) { obj.insert(obj.end(), std::declval()); }; + concept ListAlike = requires(std::remove_cvref_t& obj, T::value_type item) { obj.insert(obj.end(), std::move(item)); }; struct serial; struct serialization_file_handler @@ -124,7 +121,7 @@ public: m_expect_little_data = value; } - // Return true if small amounts of both input and output memory are expected (performance hint) + // Return true if small amounts of both input and output memory are expected (performance hint) bool expect_little_data() const { return m_expect_little_data; @@ -427,7 +424,8 @@ public: return true; } - template requires requires (T& obj) { (obj.*(&T::operator()))(std::declval>()); } + template + requires requires(T& obj, utils::serial& ar) { (obj.*(&T::operator()))(stx::exact_t(ar)); } bool serialize(T& obj) { obj(*this); @@ -568,14 +566,14 @@ public: } else if constexpr (TupleAlike) { - constexpr usz tup_size = c_tup_size; + constexpr int tup_size = c_tup_size; static_assert(tup_size == 2 || tup_size == 4, "Unimplemented tuple serialization!"); - using first_t = std::remove_cvref_t(0, tup_size - 1)>(std::declval()))>; - using second_t = std::remove_cvref_t(1, tup_size - 1)>(std::declval()))>; - using third_t = std::remove_cvref_t(2, tup_size - 1)>(std::declval()))>; - using fourth_t = std::remove_cvref_t(3, tup_size - 1)>(std::declval()))>; + using first_t = typename std::tuple_element::type; + using second_t = typename std::tuple_element::type; + using third_t = typename std::tuple_element::type; + using fourth_t = typename std::tuple_element::type; first_t first = this->operator first_t(); diff --git a/rpcs3/util/to_endian.hpp b/rpcs3/util/to_endian.hpp index 6bd101ace0..f4ec045d91 100644 --- a/rpcs3/util/to_endian.hpp +++ b/rpcs3/util/to_endian.hpp @@ -6,17 +6,18 @@ union v128; // Type converter: converts native endianness arithmetic/enum types to appropriate se_t<> type -template +template struct to_se { - template + template struct to_se_ { using type = T2; }; template - struct to_se_ || std::is_enum_v>> + requires std::is_arithmetic_v || std::is_enum_v + struct to_se_ { using type = std::conditional_t<(sizeof(T2) > 1), se_t, T2>; }; @@ -44,14 +45,16 @@ struct to_se }; template -struct to_se>> + requires(!std::is_array_v) +struct to_se { // Move const qualifier using type = const typename to_se::type; }; template -struct to_se && !std::is_const_v>> + requires(!std::is_array_v && !std::is_const_v) +struct to_se { // Move volatile qualifier using type = volatile typename to_se::type; diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index f01e558e92..0bbf45074c 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -272,14 +272,16 @@ struct alignas(16) u128 u128() noexcept = default; - template , u64> = 0> + template + requires std::is_unsigned_v constexpr u128(T arg) noexcept : lo(arg) , hi(0) { } - template , s64> = 0> + template + requires std::is_signed_v constexpr u128(T arg) noexcept : lo(s64{arg}) , hi(s64{arg} >> 63)