Merge branch 'master' into spu-giga-crash-fix

This commit is contained in:
Elad 2025-03-02 09:09:20 +02:00 committed by GitHub
commit a445443d7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
116 changed files with 2932 additions and 2035 deletions

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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)

View file

@ -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;

View file

@ -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

2
3rdparty/curl/curl vendored

@ -1 +1 @@
Subproject commit 34cf9d54a46598c44938aa7598820484d7af7133
Subproject commit 57495c64871d18905a0941db9196ef90bafe9a29

@ -1 +1 @@
Subproject commit 0024abd279d3a06435c0309a3f4172eed7c7a19a
Subproject commit 872555f4ba910252783af1507f9e7fe1653be252

View file

@ -68,7 +68,6 @@
<FloatingPointExceptions>false</FloatingPointExceptions>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
<BrowseInformation>true</BrowseInformation>
<CompileAs>CompileAsC</CompileAs>
<StringPooling>true</StringPooling>
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
@ -91,7 +90,6 @@
<FloatingPointExceptions>false</FloatingPointExceptions>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
<BrowseInformation>true</BrowseInformation>
<CompileAs>CompileAsC</CompileAs>
<StringPooling>true</StringPooling>
<MinimalRebuild>false</MinimalRebuild>

@ -1 +1 @@
Subproject commit 8236e01a9f758d15927624925c6043f84d8a261f
Subproject commit 7a44b1ab002cee6efa56d3b4c0e146b7fbaed80b

View file

@ -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")

View file

@ -84,7 +84,6 @@
<WarningLevel>$(WarningLevel)</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<BrowseInformation>true</BrowseInformation>
<FunctionLevelLinking>true</FunctionLevelLinking>
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
@ -102,7 +101,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<WholeProgramOptimization>true</WholeProgramOptimization>
<BufferSecurityCheck>false</BufferSecurityCheck>
<BrowseInformation>true</BrowseInformation>
<FunctionLevelLinking>true</FunctionLevelLinking>
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>

2
3rdparty/zstd/zstd vendored

@ -1 +1 @@
Subproject commit 97291fc5020a8994019ab76cf0cda83a9824374c
Subproject commit f8745da6ff1ad1e7bab384bd1f9d742439278e99

View file

@ -58,6 +58,7 @@
<ClCompile Include=".\zstd\programs\lorem.c" />
<ClCompile Include=".\zstd\programs\zstdcli.c" />
<ClCompile Include=".\zstd\programs\zstdcli_trace.c" />
<ClCompile Include="zstd\lib\compress\zstd_preSplit.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\zstd\lib\common\pool.h" />
@ -97,6 +98,24 @@
<ClInclude Include=".\zstd\programs\fileio.h" />
<ClInclude Include=".\zstd\programs\platform.h" />
<ClInclude Include=".\zstd\programs\util.h" />
<ClInclude Include="zstd\lib\common\allocations.h" />
<ClInclude Include="zstd\lib\common\bits.h" />
<ClInclude Include="zstd\lib\common\bitstream.h" />
<ClInclude Include="zstd\lib\common\compiler.h" />
<ClInclude Include="zstd\lib\common\cpu.h" />
<ClInclude Include="zstd\lib\common\debug.h" />
<ClInclude Include="zstd\lib\common\error_private.h" />
<ClInclude Include="zstd\lib\common\mem.h" />
<ClInclude Include="zstd\lib\common\portability_macros.h" />
<ClInclude Include="zstd\lib\common\zstd_deps.h" />
<ClInclude Include="zstd\lib\common\zstd_trace.h" />
<ClInclude Include="zstd\lib\compress\clevels.h" />
<ClInclude Include="zstd\lib\compress\hist.h" />
<ClInclude Include="zstd\lib\compress\zstd_compress_internal.h" />
<ClInclude Include="zstd\lib\compress\zstd_ldm_geartab.h" />
<ClInclude Include="zstd\lib\compress\zstd_preSplit.h" />
<ClInclude Include="zstd\lib\decompress\zstd_decompress_block.h" />
<ClInclude Include="zstd\lib\decompress\zstd_decompress_internal.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="zstd.rc" />

View file

@ -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. `<QtInstallFolder>\6.8.1\msvc2022_64\`
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\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. `<QtInstallFolder>\6.8.1\msvc2022_64\`
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\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. `<QtInstallFolder>\6.8.1\msvc2022_64`, version will fill in automatically
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\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**)

View file

@ -256,7 +256,7 @@ struct ff_t : bf_base<T, N>
#endif
template <typename T, uint I, uint N>
struct fmt_unveil<bf_t<T, I, N>, void>
struct fmt_unveil<bf_t<T, I, N>>
{
using type = typename fmt_unveil<std::common_type_t<T>>::type;
@ -267,7 +267,7 @@ struct fmt_unveil<bf_t<T, I, N>, void>
};
template <typename F, typename... Fields>
struct fmt_unveil<cf_t<F, Fields...>, void>
struct fmt_unveil<cf_t<F, Fields...>>
{
using type = typename fmt_unveil<std::common_type_t<typename F::type>>::type;
@ -278,7 +278,7 @@ struct fmt_unveil<cf_t<F, Fields...>, void>
};
template <typename T, T V, uint N>
struct fmt_unveil<ff_t<T, V, N>, void>
struct fmt_unveil<ff_t<T, V, N>>
{
using type = typename fmt_unveil<std::common_type_t<T>>::type;

View file

@ -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<stat_t>);
// 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<dir_entry>);
// Directory handle base
struct dir_base
{

View file

@ -22,7 +22,7 @@ namespace fmt
#endif
}
template <typename T, typename = void>
template <typename T>
struct fmt_unveil
{
static_assert(sizeof(T) > 0, "fmt_unveil<> error: incomplete type");
@ -54,7 +54,8 @@ struct fmt_unveil
};
template <typename T>
struct fmt_unveil<T, std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8 && alignof(T) <= 8>>
requires(std::is_integral_v<T> && sizeof(T) <= 8 && alignof(T) <= 8)
struct fmt_unveil<T>
{
using type = T;
@ -65,7 +66,8 @@ struct fmt_unveil<T, std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8 &&
};
template <typename T>
struct fmt_unveil<T, std::enable_if_t<std::is_floating_point_v<T> && sizeof(T) <= 8 && alignof(T) <= 8>>
requires(std::is_floating_point_v<T> && sizeof(T) <= 8 && alignof(T) <= 8)
struct fmt_unveil<T>
{
using type = T;
@ -77,7 +79,8 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point_v<T> && sizeof(T) <
};
template <typename T>
struct fmt_unveil<T, std::enable_if_t<std::is_enum_v<T>>>
requires std::is_enum_v<T>
struct fmt_unveil<T>
{
using type = T;
@ -88,7 +91,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_enum_v<T>>>
};
template <typename T>
struct fmt_unveil<T*, void>
struct fmt_unveil<T*>
{
using type = std::add_const_t<T>*;
@ -105,7 +108,7 @@ namespace fmt
}
template <fmt::CharT T, usz N>
struct fmt_unveil<T[N], void>
struct fmt_unveil<T[N]>
{
using type = std::add_const_t<T>*;
@ -116,7 +119,7 @@ struct fmt_unveil<T[N], void>
};
template <typename T, bool Se, usz Align>
struct fmt_unveil<se_t<T, Se, Align>, void>
struct fmt_unveil<se_t<T, Se, Align>>
{
using type = typename fmt_unveil<T>::type;
@ -127,7 +130,7 @@ struct fmt_unveil<se_t<T, Se, Align>, void>
};
// String type format provider, also type classifier (format() called if an argument is formatted as "%s")
template <typename T, typename = void>
template <typename T>
struct fmt_class_string
{
// Formatting function (must be explicitly specialized)
@ -200,47 +203,47 @@ struct fmt_class_string
};
template <>
struct fmt_class_string<const void*, void>
struct fmt_class_string<const void*>
{
static void format(std::string& out, u64 arg);
};
template <typename T>
struct fmt_class_string<T*, void> : fmt_class_string<const void*, void>
struct fmt_class_string<T*> : fmt_class_string<const void*>
{
// Classify all pointers as const void*
};
template <>
struct fmt_class_string<const char*, void>
struct fmt_class_string<const char*>
{
static void format(std::string& out, u64 arg);
};
template <>
struct fmt_class_string<char*, void> : fmt_class_string<const char*>
struct fmt_class_string<char*> : fmt_class_string<const char*>
{
// Classify char* as const char*
};
template <>
struct fmt_class_string<const char8_t*, void> : fmt_class_string<const char*>
struct fmt_class_string<const char8_t*> : fmt_class_string<const char*>
{
};
template <>
struct fmt_class_string<char8_t*, void> : fmt_class_string<const char8_t*>
struct fmt_class_string<char8_t*> : fmt_class_string<const char8_t*>
{
};
template <>
struct fmt_class_string<const wchar_t*, void>
struct fmt_class_string<const wchar_t*>
{
static void format(std::string& out, u64 arg);
};
template <>
struct fmt_class_string<wchar_t*, void> : fmt_class_string<const wchar_t*>
struct fmt_class_string<wchar_t*> : fmt_class_string<const wchar_t*>
{
};
@ -254,7 +257,7 @@ namespace fmt
}
template <fmt::StringConvertible T>
struct fmt_class_string<T, void>
struct fmt_class_string<T>
{
static FORCE_INLINE SAFE_BUFFERS(const T&) get_object(u64 arg)
{
@ -275,7 +278,7 @@ namespace fmt
}
template <fmt::ByteArray T>
struct fmt_class_string<T, void>
struct fmt_class_string<T>
{
static FORCE_INLINE SAFE_BUFFERS(const T&) get_object(u64 arg)
{

View file

@ -385,7 +385,7 @@ public:
};
template <typename T>
struct fmt_unveil<bs_t<T>, void>
struct fmt_unveil<bs_t<T>>
{
// Format as is
using type = bs_t<T>;

View file

@ -4,6 +4,29 @@
LOG_CHANNEL(log_cheat, "Cheat");
template <>
void fmt_class_string<cheat_type>::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);

11
Utilities/date_time.cpp Normal file
View file

@ -0,0 +1,11 @@
#include "stdafx.h"
#include "date_time.h"
#include <chrono>
template <>
void fmt_class_string<std::chrono::sys_time<typename std::chrono::system_clock::duration>>::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);
}

View file

@ -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

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<DebugInformationFormat>None</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
</Project>

View file

@ -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<u8*>(&m_klics[key_it]) : nullptr);
elf_file = decrypt_self(elf_file, key_it != 0 ? reinterpret_cast<u8*>(&m_klics[key_it]) : nullptr);
if (!elf_file)
{

View file

@ -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<u8[]>(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)

View file

@ -319,13 +319,13 @@ class KeyVault
std::vector<SELF_KEY> sk_LDR_arr{};
std::vector<SELF_KEY> sk_UNK7_arr{};
std::vector<SELF_KEY> sk_NPDRM_arr{};
std::unique_ptr<u8[]> 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();

View file

@ -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<const char*>(&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<const char*>(&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<const char*>(&ext_header.magic + 1), 3));
pkg_log.notice("Extended header: magic = 0x%x = \"%s\"", +ext_header.magic, std::string_view(reinterpret_cast<const char*>(&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<const u8>(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<const uchar*>(&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<PKGEntry> 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<PKGEntry>& 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<PKGEntry> entries;
if (!read_entries(entries))
{
return false;
}
std::vector<PKGEntry> entries(m_header.file_count);
std::memcpy(entries.data(), m_bufs.back().get(), entries.size() * sizeof(PKGEntry));
std::vector<u8> 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<char*>(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<std::vector<uchar>>())
fs::file tmp = fs::make_stream<std::vector<uchar>>();
{
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
{
const u64 block_size = std::min<u64>(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<std::string, install_entry*>& all_instal
m_entry_indexer = 0;
m_written_bytes = 0;
if (!decrypt_data())
usz num_failures = 0;
std::vector<PKGEntry> entries;
if (!read_entries(entries))
{
return false;
}
usz num_failures = 0;
std::vector<PKGEntry> 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<char*>(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<std::string, install_entry*>& 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<u8> read_cache;
@ -1059,23 +1143,21 @@ void package_reader::extract_worker(thread_key thread_data_key)
{
const u64 block_size = std::min<u64>({BUF_SIZE, std::max<u64>(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<const char> 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<usz>(data_span.size(), size);
std::memcpy(ptr, data_span.data(), size);
size = std::min<usz>(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<u64>(BUF_SIZE, size - read_size);
const std::span<const char> 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<u8*>(ptr) + read_size);
if (data_span.empty())
if (!advance_size)
{
break;
}
std::memcpy(static_cast<u8*>(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<u8> buffer(std::min<usz>(entry.file_size, 1u << 24));
std::vector<u8> buffer(std::min<usz>(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<package_reader>&
if (reader.m_num_failures == 0)
{
reader.m_bufs.resize(std::min<usz>(utils::get_thread_count(), reader.m_install_entries.size()));
const usz thread_count = std::min<usz>(utils::get_thread_count(), reader.m_install_entries.size());
atomic_t<usz> thread_indexer = 0;
named_thread_group workers("PKG Installer "sv, std::max<u32>(::narrow<u32>(reader.m_bufs.size()), 1) - 1, [&]()
named_thread_group workers("PKG Installer "sv, std::max<u32>(::narrow<u32>(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<const char> package_reader::archive_read_block(u64 offset, void* data_
return {static_cast<const char*>(data_ptr), read_n};
}
std::span<const char> 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<u64>(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<void*>(local_buf.get()));
const auto data_span = archive_read_block(m_header.data_offset + offset, local_buf, size);
ensure(data_span.data() == static_cast<void*>(local_buf));
// Get block count
const u64 blocks = (data_span.size() + 15) / 16;
const auto out_data = reinterpret_cast<u8*>(local_buf);
if (m_header.pkg_type == PKG_RELEASE_TYPE_DEBUG)
{
@ -1356,15 +1423,15 @@ std::span<const char> 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<const u8*>(input), sizeof(input), hash.data);
local_buf[i] ^= hash._v128;
const u128 v = read_from_ptr<u128>(out_data, i * 16);
write_to_ptr<u128>(out_data, i * 16, v ^ read_from_ptr<u128>(hash.data));
}
}
else if (m_header.pkg_type == PKG_RELEASE_TYPE_RELEASE)
@ -1384,7 +1451,8 @@ std::span<const char> package_reader::decrypt(u64 offset, u64 size, const uchar*
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const u8*>(&input), reinterpret_cast<u8*>(&key));
local_buf[i] ^= key;
const u128 v = read_from_ptr<u128>(out_data, i * 16);
write_to_ptr<u128>(out_data, i * 16, v ^ key);
}
}
else
@ -1392,8 +1460,14 @@ std::span<const char> 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<usz>(size, data_span.size());
}
int package_reader::get_progress(int maximum) const

View file

@ -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<std::string, install_entry*>::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<package_reader>& readers, std::deque<std::string>& 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<PKGEntry>& 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<std::string, install_entry*>& all_install_entries);
std::span<const char> archive_read_block(u64 offset, void* data_ptr, u64 num_bytes);
std::span<const char> 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<install_entry> 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<std::unique_ptr<u128[]>> m_bufs{};
std::array<uchar, 16> m_dec_key{};
PKGHeader m_header{};

View file

@ -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<u8[]>(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<u32>() == "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)

View file

@ -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);

View file

@ -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<u32>(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<u32>(hr));
return;
@ -215,7 +215,7 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi
if (use_default_device)
{
Microsoft::WRL::ComPtr<IMMDevice> 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<u32>(hr));
return false;
@ -319,7 +319,7 @@ f64 XAudio2Backend::GetCallbackFrameLen()
Microsoft::WRL::ComPtr<IXAudio2Extension> xaudio_ext{};
f64 min_latency{};
if (HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, std::bit_cast<void**>(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<u32>(hr));
}

View file

@ -26,7 +26,7 @@ xaudio2_enumerator::~xaudio2_enumerator()
std::vector<audio_device_enumerator::audio_device> xaudio2_enumerator::get_output_devices()
{
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> 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<u32>(hr));
return {};
@ -57,7 +57,7 @@ std::vector<audio_device_enumerator::audio_device> xaudio2_enumerator::get_outpu
for (UINT dev_idx = 0; dev_idx < count; dev_idx++)
{
Microsoft::WRL::ComPtr<IMMDevice> 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<u32>(hr));
continue;
@ -83,7 +83,7 @@ std::vector<audio_device_enumerator::audio_device> xaudio2_enumerator::get_outpu
CoTaskMemFree(id);
Microsoft::WRL::ComPtr<IPropertyStore> 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<u32>(hr));
continue;

View file

@ -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

View file

@ -110,7 +110,8 @@ protected:
virtual u32 DisAsmBranchTarget(s32 /*imm*/);
// TODO: Add builtin fmt helpper for best performance
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template <typename T>
requires std::is_integral_v<T>
static std::string SignedHex(T value)
{
const auto v = static_cast<std::make_signed_t<T>>(value);

View file

@ -1302,7 +1302,7 @@ cpu_thread* cpu_thread::get_next_cpu()
return nullptr;
}
std::shared_ptr<CPUDisAsm> make_disasm(const cpu_thread* cpu, shared_ptr<cpu_thread> handle);
extern std::shared_ptr<CPUDisAsm> make_disasm(const cpu_thread* cpu, shared_ptr<cpu_thread> handle);
void cpu_thread::dump_all(std::string& ret) const
{

View file

@ -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 <functional>
#include <unordered_map>
@ -60,9 +62,8 @@ template <typename T>
concept LLVMValue = (std::is_pointer_v<T>) && (std::is_base_of_v<llvm::Value, std::remove_pointer_t<T>>);
template <typename T>
concept DSLValue = requires (T& v)
{
{ v.eval(std::declval<llvm::IRBuilder<>*>()) } -> LLVMValue;
concept DSLValue = requires(T& v, llvm::IRBuilder<>* ir) {
{ v.eval(ir) } -> LLVMValue;
};
template <usz N>
@ -476,31 +477,33 @@ struct llvm_value_t<T[N]> : llvm_value_t<std::conditional_t<(std::extent_v<T> >
template <typename T>
using llvm_expr_t = std::decay_t<T>;
template <typename T, typename = void>
template <typename T>
struct is_llvm_expr
{
};
template <typename T>
struct is_llvm_expr<T, std::void_t<decltype(std::declval<T>().eval(std::declval<llvm::IRBuilder<>*>()))>>
template <DSLValue T>
struct is_llvm_expr<T>
{
using type = typename std::decay_t<T>::type;
};
template <typename T, typename Of, typename = void>
template <typename T, typename Of>
struct is_llvm_expr_of
{
static constexpr bool ok = false;
};
template <typename T, typename Of>
struct is_llvm_expr_of<T, Of, std::void_t<typename is_llvm_expr<T>::type, typename is_llvm_expr<Of>::type>>
requires(requires { typename is_llvm_expr<T>::type; } && requires { typename is_llvm_expr<Of>::type; })
struct is_llvm_expr_of<T, Of>
{
static constexpr bool ok = std::is_same_v<typename is_llvm_expr<T>::type, typename is_llvm_expr<Of>::type>;
};
template <typename T, typename... Types>
using llvm_common_t = std::enable_if_t<(is_llvm_expr_of<T, Types>::ok && ...), typename is_llvm_expr<T>::type>;
requires(is_llvm_expr_of<T, Types>::ok && ...)
using llvm_common_t = typename is_llvm_expr<T>::type;
template <typename... Args>
using llvm_match_tuple = decltype(std::tuple_cat(std::declval<llvm_expr_t<Args>&>().match(std::declval<llvm::Value*&>(), nullptr)...));
@ -1606,7 +1609,8 @@ struct llvm_ord
};
template <typename T>
llvm_ord(T&&) -> llvm_ord<std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value, T&&>>;
requires is_llvm_cmp<std::decay_t<T>>::value
llvm_ord(T&&) -> llvm_ord<T&&>;
template <typename Cmp, typename T = llvm_common_t<Cmp>>
struct llvm_uno
@ -1659,7 +1663,8 @@ struct llvm_uno
};
template <typename T>
llvm_uno(T&&) -> llvm_uno<std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value, T&&>>;
requires is_llvm_cmp<std::decay_t<T>>::value
llvm_uno(T&&) -> llvm_uno<T&&>;
template <typename T1, typename T2>
inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_EQ> operator ==(T1&& a1, T2&& a2)
@ -3020,7 +3025,7 @@ struct llvm_calli
if (((std::get<I>(r) = std::get<I>(a).match(v[I], _m), v[I]) && ...))
{
return std::tuple_cat(std::get<I>(r)...);
}
}
}
}
}
@ -3194,14 +3199,16 @@ public:
return {};
}
template <typename T, typename = llvm_common_t<T>>
template <typename T>
requires requires { typename llvm_common_t<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 <typename T, typename U, typename = llvm_common_t<T, U>>
template <typename T, typename U>
requires requires { typename llvm_common_t<T, U>; }
auto match_expr(T&& arg, U&& expr) -> decltype(std::tuple_cat(std::make_tuple(false), expr.match(std::declval<llvm::Value*&>(), nullptr)))
{
auto v = arg.eval(m_ir);
@ -3236,202 +3243,235 @@ public:
return expr_t<T, F>{std::forward<T>(expr), std::move(matcher)};
}
template <typename T, typename = std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value>>
template <typename T>
requires is_llvm_cmp<std::decay_t<T>>::value
static auto fcmp_ord(T&& cmp_expr)
{
return llvm_ord{std::forward<T>(cmp_expr)};
}
template <typename T, typename = std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value>>
template <typename T>
requires is_llvm_cmp<std::decay_t<T>>::value
static auto fcmp_uno(T&& cmp_expr)
{
return llvm_uno{std::forward<T>(cmp_expr)};
}
template <typename U, typename T, typename = std::enable_if_t<llvm_noncast<U, T>::is_ok>>
template <typename U, typename T>
requires llvm_noncast<U, T>::is_ok
static auto noncast(T&& expr)
{
return llvm_noncast<U, T>{std::forward<T>(expr)};
}
template <typename U, typename T, typename = std::enable_if_t<llvm_bitcast<U, T>::is_ok>>
template <typename U, typename T>
requires llvm_bitcast<U, T>::is_ok
static auto bitcast(T&& expr)
{
return llvm_bitcast<U, T>{std::forward<T>(expr)};
}
template <typename U, typename T, typename = std::enable_if_t<llvm_fpcast<U, T>::is_ok>>
template <typename U, typename T>
requires llvm_fpcast<U, T>::is_ok
static auto fpcast(T&& expr)
{
return llvm_fpcast<U, T>{std::forward<T>(expr)};
}
template <typename U, typename T, typename = std::enable_if_t<llvm_trunc<U, T>::is_ok>>
template <typename U, typename T>
requires llvm_trunc<U, T>::is_ok
static auto trunc(T&& expr)
{
return llvm_trunc<U, T>{std::forward<T>(expr)};
}
template <typename U, typename T, typename = std::enable_if_t<llvm_sext<U, T>::is_ok>>
template <typename U, typename T>
requires llvm_sext<U, T>::is_ok
static auto sext(T&& expr)
{
return llvm_sext<U, T>{std::forward<T>(expr)};
}
template <typename U, typename T, typename = std::enable_if_t<llvm_zext<U, T>::is_ok>>
template <typename U, typename T>
requires llvm_zext<U, T>::is_ok
static auto zext(T&& expr)
{
return llvm_zext<U, T>{std::forward<T>(expr)};
}
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_select<T, U, V>::is_ok>>
template <typename T, typename U, typename V>
requires llvm_select<T, U, V>::is_ok
static auto select(T&& c, U&& a, V&& b)
{
return llvm_select<T, U, V>{std::forward<T>(c), std::forward<U>(a), std::forward<V>(b)};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_min<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_min<T, U>::is_ok
static auto min(T&& a, U&& b)
{
return llvm_min<T, U>{std::forward<T>(a), std::forward<U>(b)};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_min<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_min<T, U>::is_ok
static auto max(T&& a, U&& b)
{
return llvm_max<T, U>{std::forward<T>(a), std::forward<U>(b)};
}
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_fshl<T, U, V>::is_ok>>
template <typename T, typename U, typename V>
requires llvm_fshl<T, U, V>::is_ok
static auto fshl(T&& a, U&& b, V&& c)
{
return llvm_fshl<T, U, V>{std::forward<T>(a), std::forward<U>(b), std::forward<V>(c)};
}
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_fshr<T, U, V>::is_ok>>
template <typename T, typename U, typename V>
requires llvm_fshr<T, U, V>::is_ok
static auto fshr(T&& a, U&& b, V&& c)
{
return llvm_fshr<T, U, V>{std::forward<T>(a), std::forward<U>(b), std::forward<V>(c)};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_rol<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_rol<T, U>::is_ok
static auto rol(T&& a, U&& b)
{
return llvm_rol<T, U>{std::forward<T>(a), std::forward<U>(b)};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_add_sat<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_add_sat<T, U>::is_ok
static auto add_sat(T&& a, U&& b)
{
return llvm_add_sat<T, U>{std::forward<T>(a), std::forward<U>(b)};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_sub_sat<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_sub_sat<T, U>::is_ok
static auto sub_sat(T&& a, U&& b)
{
return llvm_sub_sat<T, U>{std::forward<T>(a), std::forward<U>(b)};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_extract<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_extract<T, U>::is_ok
static auto extract(T&& v, U&& i)
{
return llvm_extract<T, U>{std::forward<T>(v), std::forward<U>(i)};
}
template <typename T, typename = std::enable_if_t<llvm_extract<T, llvm_const_int<u32>>::is_ok>>
template <typename T>
requires llvm_extract<T, llvm_const_int<u32>>::is_ok
static auto extract(T&& v, u32 i)
{
return llvm_extract<T, llvm_const_int<u32>>{std::forward<T>(v), llvm_const_int<u32>{i}};
}
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_insert<T, U, V>::is_ok>>
template <typename T, typename U, typename V>
requires llvm_insert<T, U, V>::is_ok
static auto insert(T&& v, U&& i, V&& e)
{
return llvm_insert<T, U, V>{std::forward<T>(v), std::forward<U>(i), std::forward<V>(e)};
}
template <typename T, typename V, typename = std::enable_if_t<llvm_insert<T, llvm_const_int<u32>, V>::is_ok>>
template <typename T, typename V>
requires llvm_insert<T, llvm_const_int<u32>, V>::is_ok
static auto insert(T&& v, u32 i, V&& e)
{
return llvm_insert<T, llvm_const_int<u32>, V>{std::forward<T>(v), llvm_const_int<u32>{i}, std::forward<V>(e)};
}
template <typename T, typename = std::enable_if_t<llvm_const_int<T>::is_ok>>
template <typename T>
requires llvm_const_int<T>::is_ok
static auto splat(u64 c)
{
return llvm_const_int<T>{c};
}
template <typename T, typename = std::enable_if_t<llvm_const_float<T>::is_ok>>
template <typename T>
requires llvm_const_float<T>::is_ok
static auto fsplat(f64 c)
{
return llvm_const_float<T>{c};
}
template <typename T, typename U, typename = std::enable_if_t<llvm_splat<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_splat<T, U>::is_ok
static auto vsplat(U&& v)
{
return llvm_splat<T, U>{std::forward<U>(v)};
}
template <typename T, typename... Args, typename = std::enable_if_t<llvm_const_vector<sizeof...(Args), T>::is_ok>>
template <typename T, typename... Args>
requires llvm_const_vector<sizeof...(Args), T>::is_ok
static auto build(Args... args)
{
return llvm_const_vector<sizeof...(Args), T>{static_cast<std::remove_extent_t<T>>(args)...};
}
template <typename T, typename... Args, typename = std::enable_if_t<llvm_zshuffle<sizeof...(Args), T>::is_ok>>
template <typename T, typename... Args>
requires llvm_zshuffle<sizeof...(Args), T>::is_ok
static auto zshuffle(T&& v, Args... indices)
{
return llvm_zshuffle<sizeof...(Args), T>{std::forward<T>(v), {static_cast<int>(indices)...}};
}
template <typename T, typename U, typename... Args, typename = std::enable_if_t<llvm_shuffle2<sizeof...(Args), T, U>::is_ok>>
template <typename T, typename U, typename... Args>
requires llvm_shuffle2<sizeof...(Args), T, U>::is_ok
static auto shuffle2(T&& v1, U&& v2, Args... indices)
{
return llvm_shuffle2<sizeof...(Args), T, U>{std::forward<T>(v1), std::forward<U>(v2), {static_cast<int>(indices)...}};
}
template <typename T, typename = std::enable_if_t<llvm_ctlz<T>::is_ok>>
template <typename T>
requires llvm_ctlz<T>::is_ok
static auto ctlz(T&& a)
{
return llvm_ctlz<T>{std::forward<T>(a)};
}
template <typename T, typename = std::enable_if_t<llvm_ctpop<T>::is_ok>>
template <typename T>
requires llvm_ctpop<T>::is_ok
static auto ctpop(T&& a)
{
return llvm_ctpop<T>{std::forward<T>(a)};
}
// Average: (a + b + 1) >> 1
template <typename T, typename U, typename = std::enable_if_t<llvm_avg<T, U>::is_ok>>
template <typename T, typename U>
requires llvm_avg<T, U>::is_ok
static auto avg(T&& a, U&& b)
{
return llvm_avg<T, U>{std::forward<T>(a), std::forward<U>(b)};
}
template <typename T, typename = std::enable_if_t<llvm_fsqrt<T>::is_ok>>
template <typename T>
requires llvm_fsqrt<T>::is_ok
static auto fsqrt(T&& a)
{
return llvm_fsqrt<T>{std::forward<T>(a)};
}
template <typename T, typename = std::enable_if_t<llvm_fabs<T>::is_ok>>
template <typename T>
requires llvm_fabs<T>::is_ok
static auto fabs(T&& a)
{
return llvm_fabs<T>{std::forward<T>(a)};
}
// Optionally opportunistic hardware FMA, can be used if results are identical for all possible input values
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_fmuladd<T, U, V>::is_ok>>
template <typename T, typename U, typename V>
requires llvm_fmuladd<T, U, V>::is_ok
static auto fmuladd(T&& a, U&& b, V&& c, bool strict_fma)
{
return llvm_fmuladd<T, U, V>{std::forward<T>(a), std::forward<U>(b), std::forward<V>(c), strict_fma};
}
// Opportunistic hardware FMA, can be used if results are identical for all possible input values
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_fmuladd<T, U, V>::is_ok>>
template <typename T, typename U, typename V>
requires llvm_fmuladd<T, U, V>::is_ok
auto fmuladd(T&& a, U&& b, V&& c)
{
return llvm_fmuladd<T, U, V>{std::forward<T>(a), std::forward<U>(b), std::forward<V>(c), m_use_fma};
@ -3754,7 +3794,8 @@ public:
return load_const(g, i, get_type<T>());
}
template <typename T, typename I> requires requires () { std::declval<I>().eval(std::declval<llvm::IRBuilder<>*>()); }
template <typename T, typename I>
requires requires(I& i, llvm::IRBuilder<>* ir) { i.eval(ir); }
value_t<T> load_const(llvm::GlobalVariable* g, I i)
{
value_t<T> result;
@ -3873,7 +3914,8 @@ public:
return llvm_calli<u8[16], T, U, V>{"any_select_by_bit4", {std::forward<T>(m), std::forward<U>(a), std::forward<V>(b)}};
}
template <typename T, typename = std::enable_if_t<std::is_same_v<llvm_common_t<T>, f32[4]>>>
template <typename T>
requires std::is_same_v<llvm_common_t<T>, f32[4]>
static auto fre(T&& a)
{
#if defined(ARCH_X64)
@ -3883,7 +3925,8 @@ public:
#endif
}
template <typename T, typename = std::enable_if_t<std::is_same_v<llvm_common_t<T>, f32[4]>>>
template <typename T>
requires std::is_same_v<llvm_common_t<T>, f32[4]>
static auto frsqe(T&& a)
{
#if defined(ARCH_X64)
@ -3893,7 +3936,8 @@ public:
#endif
}
template <typename T, typename U, typename = std::enable_if_t<std::is_same_v<llvm_common_t<T, U>, f32[4]>>>
template <typename T, typename U>
requires std::is_same_v<llvm_common_t<T, U>, f32[4]>
static auto fmax(T&& a, U&& b)
{
#if defined(ARCH_X64)
@ -3903,7 +3947,8 @@ public:
#endif
}
template <typename T, typename U, typename = std::enable_if_t<std::is_same_v<llvm_common_t<T, U>, f32[4]>>>
template <typename T, typename U>
requires std::is_same_v<llvm_common_t<T, U>, f32[4]>
static auto fmin(T&& a, U&& b)
{
#if defined(ARCH_X64)
@ -3913,13 +3958,15 @@ public:
#endif
}
template <typename T, typename U, typename = std::enable_if_t<std::is_same_v<llvm_common_t<T, U>, u8[16]>>>
template <typename T, typename U>
requires std::is_same_v<llvm_common_t<T, U>, u8[16]>
static auto vdbpsadbw(T&& a, U&& b, u8 c)
{
return llvm_calli<u16[8], T, U, llvm_const_int<u32>>{"llvm.x86.avx512.dbpsadbw.128", {std::forward<T>(a), std::forward<U>(b), llvm_const_int<u32>{c}}};
}
template <typename T, typename U, typename = std::enable_if_t<std::is_same_v<llvm_common_t<T, U>, f32[4]>>>
template <typename T, typename U>
requires std::is_same_v<llvm_common_t<T, U>, f32[4]>
static auto vrangeps(T&& a, U&& b, u8 c, u8 d)
{
return llvm_calli<f32[4], T, U, llvm_const_int<u32>, T, llvm_const_int<u8>>{"llvm.x86.avx512.mask.range.ps.128", {std::forward<T>(a), std::forward<U>(b), llvm_const_int<u32>{c}, std::forward<T>(a), llvm_const_int<u8>{d}}};
@ -3928,7 +3975,7 @@ public:
// Format llvm::SizeType
template <>
struct fmt_unveil<llvm::TypeSize, void>
struct fmt_unveil<llvm::TypeSize>
{
using type = usz;

View file

@ -82,11 +82,11 @@ constexpr FORCE_INLINE CellNotAnError not_an_error(const T& value)
return static_cast<CellNotAnError>(static_cast<s32>(value));
}
template <typename T, typename>
template <typename T>
struct ppu_gpr_cast_impl;
template <>
struct ppu_gpr_cast_impl<error_code, void>
struct ppu_gpr_cast_impl<error_code>
{
static inline u64 to(const error_code& code)
{

View file

@ -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);

View file

@ -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<u64> value;
};
template<typename T, typename>
template <typename T>
struct ppu_gpr_cast_impl;
template<>
struct ppu_gpr_cast_impl<CellSailEvent, void>
template <>
struct ppu_gpr_cast_impl<CellSailEvent>
{
static inline u64 to(const CellSailEvent& event)
{

View file

@ -3,6 +3,7 @@
#include "util/types.hpp"
#include "Emu/Memory/vm_ptr.h"
#include "Emu/Cell/ErrorCodes.h"
#include <mutex>
#include <vector>
#include <mutex>

View file

@ -12,7 +12,7 @@
LOG_CHANNEL(ppu_validator);
const ppu_decoder<ppu_itype> s_ppu_itype;
extern const ppu_decoder<ppu_itype> g_ppu_itype;
template<>
void fmt_class_string<ppu_attr>::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<lv2_obj>::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<lv2_obj>::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<u32>(addr)) == ppu_itype::UNK)
if (addr < start || addr >= end || g_ppu_itype.decode(*get_ptr<u32>(addr)) == ppu_itype::UNK)
{
if (!fmap.contains(addr))
{
@ -1337,7 +1343,7 @@ bool ppu_module<lv2_obj>::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<lv2_obj>::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_local_info_t> 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<u32> 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<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
const u32 iaddr = addr;
const ppu_opcode_t op{get_ref<u32>(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<lv2_obj>::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<u32> _ptr = vm::cast(func.addr);
@ -1760,7 +1809,7 @@ bool ppu_module<lv2_obj>::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<u32>(block_queue.size() - 1);
@ -1769,6 +1818,15 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
return umax;
};
std::map<u32, u32> 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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::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<u32>(static_cast<u32>(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<lv2_obj>::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<lv2_obj>::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<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
{
const u32 iaddr = _ptr.addr();
const ppu_opcode_t op{get_ref<u32>(_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<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
{
const u32 addr = _ptr.addr();
const ppu_opcode_t op{get_ref<u32>(_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<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
{
const ppu_opcode_t op{get_ref<u32>(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<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
}
const ppu_opcode_t test_op{get_ref<u32>(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<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
break;
}
const auto type1 = s_ppu_itype.decode(get_ref<u32>(target + 4));
const auto type1 = g_ppu_itype.decode(get_ref<u32>(target + 4));
if (type1 == ppu_itype::UNK)
{

View file

@ -106,6 +106,11 @@ struct ppu_segment
void* ptr{};
};
struct ppua_reg_mask_t
{
u64 mask;
};
// PPU Module Information
template <typename Type>
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<u32, u32> local_bounds{0, u32{umax}}; // Module addresses range
std::shared_ptr<std::pair<u32, u32>> jit_bounds; // JIT instance modules addresses range
std::unordered_map<u32, void*> imports; // Imports information for release upon unload (TODO: OVL implementation!)
std::map<u32, std::vector<std::pair<ppua_reg_mask_t, u64>>> stub_addr_to_constant_state_of_registers; // Tells possible constant states of registers of functions
bool is_relocatable = false; // Is code relocatable(?)
template <typename T>

View file

@ -71,6 +71,25 @@ extern u32 ppu_generate_id(std::string_view name)
return result;
}
static void select_from_nids_scenpdrm_addrs(std::map<u32, std::vector<std::pair<ppua_reg_mask_t, u64>>>& result, const std::unordered_map<u32, u32>& 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<u32, info> functions{};
std::map<u32, info> variables{};
// Obsolete
bool imported = false;
};
// Module map
@ -940,9 +956,12 @@ static auto ppu_load_exports(const ppu_module<lv2_obj>& _module, ppu_linkage_inf
return result;
}
static auto ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu_reloc>& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end)
using import_result_t = std::pair<std::unordered_map<u32, void*>, std::unordered_map<u32, u32>>;
static import_result_t ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu_reloc>& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end)
{
std::unordered_map<u32, void*> 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<lv2_obj>& _module, std::vector<ppu
ppu_loader.notice("**** %s import: [%s] (0x%08x) -> 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<ppu_function_manager>().addr;
@ -992,7 +1017,7 @@ static auto ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu
// Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info)
if (const u32 frefs = (lib.attributes & 0x2000) ? +_module.get_ref<u32>(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<lv2_obj>& _module, std::vector<ppu
ppu_loader.notice("**** %s import: &[%s] (ref=*0x%x)", module_name, ppu_get_variable_name(module_name, vnid), vref);
// Variable linkage info
auto& vlink = link->modules[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<lv2_prx> 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<u32, u32> 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<u32, u32> 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<shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_ob
fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic);
}
std::unordered_map<u32, u32> 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;
}

View file

@ -4162,6 +4162,52 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
const u32 software_thread_limit = std::min<u32>(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<u32>(utils::get_thread_count(), 2) - 1 : utils::get_thread_count(); // One LLVM thread less
std::vector<u128> 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<const u8>(static_cast<u32>(constant_value), 16))
{
// Try to read from that address
if (const u128 klic_value = read_from_ptr<u128>(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<u32>(software_thread_limit, cpu_thread_limit), [&]
{
#ifdef __APPLE__
@ -4211,12 +4257,48 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
fmt::append(path, "_x%x", off);
}
// Some files may fail to decrypt due to the lack of klic
src = decrypt_self(std::move(src));
for (usz i = 0;; i++)
{
if (i > 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<const u8*>(&key)))
{
src = std::move(result);
break;
}
}
if (!src && !Emu.klic.empty() && src.open(path))
{
src = decrypt_self(src, reinterpret_cast<u8*>(&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<std::string>& dir_queue, std::vector<ppu_
continue;
}
// Some files may fail to decrypt due to the lack of klic
src = decrypt_self(std::move(src), nullptr, nullptr, true);
for (usz i = 0;; i++)
{
if (i > 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<const u8*>(&key)))
{
src = std::move(result);
break;
}
}
if (!src && !Emu.klic.empty() && src.open(path))
{
src = decrypt_self(src, reinterpret_cast<u8*>(&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<ppu_module<lv2_obj>*> module_list;
module_list.emplace_back(&g_fxo->get<main_ppu_module<lv2_obj>>());
const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/");

View file

@ -382,14 +382,15 @@ public:
static_assert(ppu_join_status::max <= ppu_join_status{ppu_thread::id_base});
template<typename T, typename = void>
template <typename T>
struct ppu_gpr_cast_impl
{
static_assert(!sizeof(T), "Invalid type for ppu_gpr_cast<>");
};
template<typename T>
struct ppu_gpr_cast_impl<T, std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>>
template <typename T>
requires std::is_integral_v<T> || std::is_enum_v<T>
struct ppu_gpr_cast_impl<T>
{
static_assert(sizeof(T) <= 8, "Too big integral type for ppu_gpr_cast<>()");
static_assert(std::is_same_v<std::decay_t<T>, bool> == false, "bool type is deprecated in ppu_gpr_cast<>(), use b8 instead");
@ -405,8 +406,8 @@ struct ppu_gpr_cast_impl<T, std::enable_if_t<std::is_integral_v<T> || std::is_en
}
};
template<>
struct ppu_gpr_cast_impl<b8, void>
template <>
struct ppu_gpr_cast_impl<b8>
{
static inline u64 to(const b8& value)
{
@ -419,8 +420,8 @@ struct ppu_gpr_cast_impl<b8, void>
}
};
template<typename T, typename AT>
struct ppu_gpr_cast_impl<vm::_ptr_base<T, AT>, void>
template <typename T, typename AT>
struct ppu_gpr_cast_impl<vm::_ptr_base<T, AT>>
{
static inline u64 to(const vm::_ptr_base<T, AT>& value)
{
@ -433,8 +434,8 @@ struct ppu_gpr_cast_impl<vm::_ptr_base<T, AT>, void>
}
};
template<typename T, typename AT>
struct ppu_gpr_cast_impl<vm::_ref_base<T, AT>, void>
template <typename T, typename AT>
struct ppu_gpr_cast_impl<vm::_ref_base<T, AT>>
{
static inline u64 to(const vm::_ref_base<T, AT>& value)
{
@ -448,7 +449,7 @@ struct ppu_gpr_cast_impl<vm::_ref_base<T, AT>, void>
};
template <>
struct ppu_gpr_cast_impl<vm::null_t, void>
struct ppu_gpr_cast_impl<vm::null_t>
{
static inline u64 to(const vm::null_t& /*value*/)
{

View file

@ -6234,14 +6234,14 @@ public:
const value_t<f32[4]> 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<s32[4]>(bitcast<s32[4]>(a) > bitcast<s32[4]>(b)));
}
if (safe_finite_compare.test(1))
{
return eval(sext<s32[4]>(fcmp_uno(clamp_negative_smax(a) > b)));
}
if (safe_finite_compare.test(0))
{
return eval(sext<s32[4]>(fcmp_ord(a > clamp_smax(b))));
}
const auto ai = eval(bitcast<s32[4]>(a));
const auto bi = eval(bitcast<s32[4]>(b));
if (!safe_nonzero_compare.any())
{
return eval(sext<s32[4]>(fcmp_uno(a != b) & select((ai & bi) >= 0, ai > bi, ai < bi)));
}
else
{
return eval(sext<s32[4]>(select((ai & bi) >= 0, ai > bi, ai < bi)));
}
return eval(sext<s32[4]>(fcmp_uno(a != b) & select((ai & bi) >= 0, ai > bi, ai < bi)));
});
set_vr(op.rt, fcgt(get_vr<f32[4]>(op.ra), get_vr<f32[4]>(op.rb)));

View file

@ -36,7 +36,7 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic));
if (!src)
{

View file

@ -265,7 +265,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic));
if (!src)
{

View file

@ -192,7 +192,6 @@ struct lv2_prx final : ppu_module<lv2_obj>
shared_mutex mutex;
std::unordered_map<u32, u32> specials;
std::unordered_map<u32, void*> imports;
vm::ptr<s32(u32 argc, vm::ptr<void> argv)> start = vm::null;
vm::ptr<s32(u32 argc, vm::ptr<void> argv)> stop = vm::null;

View file

@ -26,7 +26,7 @@ template <typename T>
concept IdmBaseCompatible = (std::is_final_v<T> ? IdmCompatible<T> : !!(requires () { u32{T::id_step}, u32{T::id_count}; }));
template <typename T>
concept IdmSavable = IdmBaseCompatible<T> && T::savestate_init_pos != 0 && (requires () { std::declval<T>().save(std::declval<stx::exact_t<utils::serial&>>()); });
concept IdmSavable = IdmBaseCompatible<T> && T::savestate_init_pos != 0 && (requires(T& t, utils::serial& ar) { t.save(stx::exact_t<utils::serial&>(ar)); });
// If id_base is declared in base type, than storage type must declare id_type
template <typename Base, typename Type>
@ -105,7 +105,7 @@ namespace id_manager
}
// ID traits
template <typename T, typename = void>
template <typename T>
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 <typename T>
struct id_traits_load_func<T, std::void_t<decltype(&T::load)>>
requires requires() { &T::load; }
struct id_traits_load_func<T>
{
static constexpr pointer_keeper(*load)(utils::serial&) = [](utils::serial& ar) -> pointer_keeper
{
@ -134,14 +135,15 @@ namespace id_manager
};
};
template <typename T, typename = void>
template <typename T>
struct id_traits_savable_func
{
static constexpr bool(*savable)(void*) = [](void*) -> bool { return true; };
};
template <typename T>
struct id_traits_savable_func<T, std::void_t<decltype(&T::savable)>>
requires requires { &T::savable; }
struct id_traits_savable_func<T>
{
static constexpr bool(*savable)(void* ptr) = [](void* ptr) -> bool { return static_cast<const T*>(ptr)->savable(); };
};

View file

@ -342,21 +342,24 @@ namespace vm
template<typename T, typename AT = u32, typename AT2 = u32> using bcpptr = bpptr<const T, AT, AT2>;
// Perform static_cast (for example, vm::ptr<void> to vm::ptr<char>)
template<typename CT, typename T, typename AT, typename = decltype(static_cast<to_be_t<CT>*>(std::declval<T*>()))>
template <typename CT, typename T, typename AT>
requires requires(T* t) { static_cast<to_be_t<CT>*>(t); }
inline _ptr_base<to_be_t<CT>, u32> static_ptr_cast(const _ptr_base<T, AT>& other)
{
return vm::cast(other.addr());
}
// Perform const_cast (for example, vm::cptr<char> to vm::ptr<char>)
template<typename CT, typename T, typename AT, typename = decltype(const_cast<to_be_t<CT>*>(std::declval<T*>()))>
template <typename CT, typename T, typename AT>
requires requires(T* t) { const_cast<to_be_t<CT>*>(t); }
inline _ptr_base<to_be_t<CT>, u32> const_ptr_cast(const _ptr_base<T, AT>& other)
{
return vm::cast(other.addr());
}
// Perform reinterpret cast
template <typename CT, typename T, typename AT, typename = decltype(reinterpret_cast<to_be_t<CT>*>(std::declval<T*>()))>
template <typename CT, typename T, typename AT>
requires requires(T* t) { reinterpret_cast<to_be_t<CT>*>(t); }
inline _ptr_base<to_be_t<CT>, u32> unsafe_ptr_cast(const _ptr_base<T, AT>& other)
{
return vm::cast(other.addr());
@ -426,8 +429,8 @@ struct to_se<vm::_ptr_base<T, AT>, Se>
};
// Format pointer
template<typename T, typename AT>
struct fmt_unveil<vm::_ptr_base<T, AT>, void>
template <typename T, typename AT>
struct fmt_unveil<vm::_ptr_base<T, AT>>
{
using type = vm::_ptr_base<T, u32>; // Use only T, ignoring AT
@ -438,37 +441,37 @@ struct fmt_unveil<vm::_ptr_base<T, AT>, void>
};
template <>
struct fmt_class_string<vm::_ptr_base<const void, u32>, void>
struct fmt_class_string<vm::_ptr_base<const void, u32>>
{
static void format(std::string& out, u64 arg);
};
template <typename T>
struct fmt_class_string<vm::_ptr_base<T, u32>, void> : fmt_class_string<vm::_ptr_base<const void, u32>, void>
struct fmt_class_string<vm::_ptr_base<T, u32>> : fmt_class_string<vm::_ptr_base<const void, u32>>
{
// Classify all pointers as const void*
};
template <>
struct fmt_class_string<vm::_ptr_base<const char, u32>, void>
struct fmt_class_string<vm::_ptr_base<const char, u32>>
{
static void format(std::string& out, u64 arg);
};
template <>
struct fmt_class_string<vm::_ptr_base<char, u32>, void> : fmt_class_string<vm::_ptr_base<const char, u32>>
struct fmt_class_string<vm::_ptr_base<char, u32>> : fmt_class_string<vm::_ptr_base<const char, u32>>
{
// Classify char* as const char*
};
template <usz Size>
struct fmt_class_string<vm::_ptr_base<const char[Size], u32>, void> : fmt_class_string<vm::_ptr_base<const char, u32>>
struct fmt_class_string<vm::_ptr_base<const char[Size], u32>> : fmt_class_string<vm::_ptr_base<const char, u32>>
{
// Classify const char[] as const char*
};
template <usz Size>
struct fmt_class_string<vm::_ptr_base<char[Size], u32>, void> : fmt_class_string<vm::_ptr_base<const char, u32>>
struct fmt_class_string<vm::_ptr_base<char[Size], u32>> : fmt_class_string<vm::_ptr_base<const char, u32>>
{
// Classify char[] as const char*
};

View file

@ -193,8 +193,8 @@ struct to_se<vm::_ref_base<T, AT>, Se>
};
// Forbid formatting
template<typename T, typename AT>
struct fmt_unveil<vm::_ref_base<T, AT>, void>
template <typename T, typename AT>
struct fmt_unveil<vm::_ref_base<T, AT>>
{
static_assert(!sizeof(T), "vm::_ref_base<>: ambiguous format argument");
};

View file

@ -500,12 +500,13 @@ struct copy_rgb655_block_swizzled
struct copy_decoded_bc1_block
{
static void copy_mipmap_level(std::span<u32> dst, std::span<const u64> 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<u32> dst, std::span<const u64> 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<const u8*>(&src[src_offset + col]);
u8* decompressedBlock = reinterpret_cast<u8*>(&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<u32> dst, std::span<const u128> 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<u32> dst, std::span<const u128> 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<const u8*>(&src[src_offset + col]);
u8* decompressedBlock = reinterpret_cast<u8*>(&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<u32> dst, std::span<const u128> 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<u32> dst, std::span<const u128> 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<const u8*>(&src[src_offset + col]);
u8* decompressedBlock = reinterpret_cast<u8*>(&dst[dst_offset + col * 4]);
bcdec_bc3(compressedBlock, decompressedBlock, destinationPitch);

View file

@ -94,11 +94,11 @@ namespace rsx
return value;
}
template<typename = std::enable_if<!std::is_same_v<T, bool>>>
operator bool() const
{
return error.empty();
}
operator bool() const
requires(!std::is_same_v<T, bool>)
{
return error.empty();
}
operator std::pair<T&, E&>() const
{

View file

@ -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, u32>(enum_type::read, flags::cause_is_read),
std::make_pair<enum_type, u32>(enum_type::deferred_read, flags::cause_is_read | flags::cause_is_deferred),
std::make_pair<enum_type, u32>(enum_type::write, flags::cause_is_write),
std::make_pair<enum_type, u32>(enum_type::deferred_write, flags::cause_is_write | flags::cause_is_deferred),
std::make_pair<enum_type, u32>(enum_type::unmap, flags::cause_keeps_fault_range_protection | flags::cause_skips_flush),
std::make_pair<enum_type, u32>(enum_type::reprotect, flags::cause_keeps_fault_range_protection),
std::make_pair<enum_type, u32>(enum_type::superseded_by_fbo, flags::cause_keeps_fault_range_protection | flags::cause_skips_fbos | flags::cause_skips_flush),
std::make_pair<enum_type, u32>(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;
}
}
}

View file

@ -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, u32>(enum_type::read, flags::cause_is_read),
std::make_pair<enum_type, u32>(enum_type::deferred_read, flags::cause_is_read | flags::cause_is_deferred),
std::make_pair<enum_type, u32>(enum_type::write, flags::cause_is_write),
std::make_pair<enum_type, u32>(enum_type::deferred_write, flags::cause_is_write | flags::cause_is_deferred),
std::make_pair<enum_type, u32>(enum_type::unmap, flags::cause_keeps_fault_range_protection | flags::cause_skips_flush),
std::make_pair<enum_type, u32>(enum_type::reprotect, flags::cause_keeps_fault_range_protection),
std::make_pair<enum_type, u32>(enum_type::superseded_by_fbo, flags::cause_keeps_fault_range_protection | flags::cause_skips_fbos | flags::cause_skips_flush),
std::make_pair<enum_type, u32>(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);
};
}

View file

@ -0,0 +1,10 @@
#include "stdafx.h"
#include "GSFrameBase.h"
#include "Emu/system_config.h"
atomic_t<bool> g_game_window_focused = false;
bool is_input_allowed()
{
return g_game_window_focused || g_cfg.io.background_input_enabled;
}

View file

@ -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<u8> 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<u8*>(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)

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "ProgramStateCache.h"
#include "Emu/system_config.h"
#include "util/sysinfo.hpp"
#include <stack>
@ -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<const __m512i*>(&program.instruction_mask);
const __m128i* instMask128 = reinterpret_cast<const __m128i*>(&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<const __m512i*>(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<const __m512i*>(&binary1.instruction_mask);
const __m128i* instMask128 = reinterpret_cast<const __m128i*>(&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<const __m512i*>(binary1.data.data());
const __m512i* instBuffer2 = reinterpret_cast<const __m512i*>(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])
{

View file

@ -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"

View file

@ -1,5 +1,5 @@
#include "device.h"
#include "instance.hpp"
#include "instance.h"
#include "util/logs.hpp"
#include "Emu/system_config.h"

View file

@ -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<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT"));
_vkDestroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(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<const char*> extensions;
std::vector<const char*> 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<VkLayerSettingEXT> 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<uint32_t>(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<u32>(layers.size());
instance_info.ppEnabledLayerNames = layers.data();
instance_info.enabledExtensionCount = fast ? 0 : static_cast<u32>(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<physical_device>& 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<VkPhysicalDevice> 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<VkBool32> 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<VkSurfaceFormatKHR> 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);
}
}

View file

@ -0,0 +1,64 @@
#pragma once
#include "../VulkanAPI.h"
#include "swapchain.h"
#include <algorithm>
#include <vector>
#ifdef __APPLE__
#include <MoltenVK/mvk_vulkan.h>
#include <MoltenVK/mvk_private_api.h>
#endif
namespace vk
{
class supported_extensions
{
private:
std::vector<VkExtensionProperties> 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<physical_device> 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<physical_device>& enumerate_devices();
swapchain_base* create_swapchain(display_handle_t window_handle, vk::physical_device& dev);
};
}

View file

@ -1,457 +0,0 @@
#pragma once
#include "../VulkanAPI.h"
#include "swapchain.hpp"
#include <algorithm>
#include <vector>
#ifdef __APPLE__
#include <MoltenVK/mvk_vulkan.h>
#include <MoltenVK/mvk_private_api.h>
#endif
namespace vk
{
class supported_extensions
{
private:
std::vector<VkExtensionProperties> 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<physical_device> 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<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT"));
_vkDestroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(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<const char*> extensions;
std::vector<const char*> 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<VkLayerSettingEXT> 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<uint32_t>(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<u32>(layers.size());
instance_info.ppEnabledLayerNames = layers.data();
instance_info.enabledExtensionCount = fast ? 0 : static_cast<u32>(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<physical_device>& 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<VkPhysicalDevice> 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<decltype(p)>;
#ifdef HAVE_X11
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<T, std::pair<wl_display*, wl_surface*>>)
{
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<true, std::false_type, T>::value, "Unhandled window_handle type in std::variant");
}
}, window_handle);
#endif
u32 device_queues = dev.get_queue_count();
std::vector<VkBool32> 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<VkSurfaceFormatKHR> 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);
}
};
}

View file

@ -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<buffer>(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, &copyRegion);
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<swapchain_image_RPCS3>(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<VkImage> 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<PFN_vkCreateSwapchainKHR>(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(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<VkSurfaceCapabilitiesKHR, bool> 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<PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR>(
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<vk::physical_device&>(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<VkPresentModeKHR> 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<VkPresentModeKHR> 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<int>(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<int>(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 = &image;
if (semaphore != VK_NULL_HANDLE)
{
present.waitSemaphoreCount = 1;
present.pWaitSemaphores = &semaphore;
}
return _vkQueuePresentKHR(dev.get_present_queue(), &present);
}
}

View file

@ -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

View file

@ -1,786 +0,0 @@
#pragma once
#ifdef HAVE_X11
#include <X11/Xutil.h>
#endif
#include "../../display.h"
#include "../VulkanAPI.h"
#include "image.h"
#include <memory>
namespace vk
{
struct swapchain_image_WSI
{
VkImage value = VK_NULL_HANDLE;
};
class swapchain_image_RPCS3 : public image
{
std::unique_ptr<buffer> 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<buffer>(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, &copyRegion);
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<typename T>
class abstract_swapchain_impl : public swapchain_base
{
protected:
std::vector<T> 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<std::pair<bool, std::unique_ptr<swapchain_image_RPCS3>>>;
using WSI_swapchain_base = abstract_swapchain_impl<swapchain_image_WSI>;
#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<decltype(p)>;
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<char*>(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<swapchain_image_RPCS3>(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<VkImage> 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<PFN_vkCreateSwapchainKHR>(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(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<VkSurfaceCapabilitiesKHR, bool> 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<PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR>(
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<vk::physical_device&>(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<VkPresentModeKHR> 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<VkPresentModeKHR> 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<int>(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<int>(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 = &image;
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;
}
};
}

View file

@ -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
}

View file

@ -0,0 +1,227 @@
#pragma once
#ifdef HAVE_X11
#include <X11/Xutil.h>
#endif
#include "../../display.h"
#include "../VulkanAPI.h"
#include "image.h"
#include <memory>
namespace vk
{
struct swapchain_image_WSI
{
VkImage value = VK_NULL_HANDLE;
};
class swapchain_image_RPCS3 : public image
{
std::unique_ptr<buffer> 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<typename T>
class abstract_swapchain_impl : public swapchain_base
{
protected:
std::vector<T> 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<swapchain_image_WSI>;
class native_swapchain_base : public abstract_swapchain_impl<std::pair<bool, std::unique_ptr<swapchain_image_RPCS3>>>
{
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<VkSurfaceCapabilitiesKHR, bool> 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;
};
}

View file

@ -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
}

View file

@ -0,0 +1,172 @@
#pragma once
#include "swapchain_core.h"
#ifdef HAVE_X11
#include <X11/Xutil.h>
#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<decltype(p)>;
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<char*>(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<decltype(p)>;
#ifdef HAVE_X11
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<T, std::pair<wl_display*, wl_surface*>>)
{
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<true, std::false_type, T>::value, "Unhandled window_handle type in std::variant");
}
}, window_handle);
return ensure(result, "Failed to initialize Vulkan display surface");
}
}

View file

@ -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
}

View file

@ -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)

View file

@ -213,9 +213,9 @@ namespace rsx
};
template <typename T>
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<T*>(input_pixels);
const T *src = static_cast<const T*>(input_pixels);
T *dst = static_cast<T*>(output_pixels);
for (u16 h = 0; h < input_height; ++h)
@ -336,8 +336,8 @@ namespace rsx
template <typename T, bool input_is_swizzled>
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 <bool clamp = false>
static inline const std::pair<u16, u16> 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)

View file

@ -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<shared_ptr<lv2_overlay>, 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<named_thread>("SPRX Loader"sv, [this, dir_queue]() mutable
{
std::vector<ppu_module<lv2_obj>*> mod_list;
if (auto& _main = *ensure(g_fxo->try_get<main_ppu_module<lv2_obj>>()); !_main.path.empty())
{
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, std::vector<u32>{}, [](){ 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<std::string> 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<u8*>(&klic[0]), &g_ps3_process_info.self_info);
elf_file = decrypt_self(elf_file, klic.empty() ? nullptr : reinterpret_cast<u8*>(&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)
{

View file

@ -107,6 +107,9 @@ struct EmuCallbacks
std::function<std::vector<std::string>()> get_font_dirs;
std::function<bool(const std::vector<std::string>&)> on_install_pkgs;
std::function<void(u32)> add_breakpoint;
std::function<bool()> display_sleep_control_supported;
std::function<void(bool)> enable_display_sleep;
std::function<void()> check_microphone_permissions;
};
namespace utils

View file

@ -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<class T>
T getProc(HMODULE hModule, LPCSTR lpProcName)
{
return reinterpret_cast<T>(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<PFN_XINPUTGETEXTENDED>(library, "XInputGetExtended"); // Optional
xinputGetCustomData = getProc<PFN_XINPUTGETCUSTOMDATA>(library, "XInputGetCustomData"); // Optional
xinputGetState = getProc<PFN_XINPUTGETSTATE>(library, reinterpret_cast<LPCSTR>(100));
xinputGetExtended = library.get<PFN_XINPUTGETEXTENDED>("XInputGetExtended"); // Optional
xinputGetCustomData = library.get<PFN_XINPUTGETCUSTOMDATA>("XInputGetCustomData"); // Optional
xinputGetState = library.get<PFN_XINPUTGETSTATE>(reinterpret_cast<LPCSTR>(100));
if (!xinputGetState)
xinputGetState = getProc<PFN_XINPUTGETSTATE>(library, "XInputGetState");
xinputGetState = library.get<PFN_XINPUTGETSTATE>("XInputGetState");
xinputSetState = getProc<PFN_XINPUTSETSTATE>(library, "XInputSetState");
xinputGetBatteryInformation = getProc<PFN_XINPUTGETBATTERYINFORMATION>(library, "XInputGetBatteryInformation");
xinputSetState = library.get<PFN_XINPUTSETSTATE>("XInputSetState");
xinputGetBatteryInformation = library.get<PFN_XINPUTGETBATTERYINFORMATION>("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;

View file

@ -1,6 +1,7 @@
#pragma once
#include "Emu/Io/PadHandler.h"
#include "util/dyn_lib.hpp"
#include <unordered_map>
@ -9,7 +10,6 @@
#endif
#include <Windows.h>
#include <Xinput.h>
#include <chrono>
// 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<PadDevice> get_device(const std::string& device) override;
bool get_is_left_trigger(const std::shared_ptr<PadDevice>& device, u64 keyCode) override;

View file

@ -49,7 +49,12 @@
<ClInclude Include="Emu\RSX\VK\vkutils\image.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\image_helpers.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\scratch.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_android.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_core.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_unix.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_macos.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_win32.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\sync.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\graphics_pipeline_state.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\memory.h" />
@ -58,7 +63,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\query_pool.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\sampler.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\shared.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\instance.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\instance.h" />
<ClInclude Include="Emu\RSX\VK\VKVertexProgram.h" />
<ClInclude Include="Emu\RSX\VK\VulkanAPI.h" />
</ItemGroup>
@ -94,7 +99,9 @@
<ClCompile Include="Emu\RSX\VK\vkutils\data_heap.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\image.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\image_helpers.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\instance.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\scratch.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\swapchain.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\sync.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\memory.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\descriptors.cpp" />

View file

@ -73,6 +73,12 @@
<Filter>upscalers\fsr1</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKCommonPipelineLayout.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\instance.cpp">
<Filter>vkutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\vkutils\swapchain.cpp">
<Filter>vkutils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKCommonDecompiler.h" />
@ -124,7 +130,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\device.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\instance.hpp">
<ClInclude Include="Emu\RSX\VK\vkutils\instance.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\memory.h">
@ -139,7 +145,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\image.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.hpp">
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\descriptors.h">
@ -175,6 +181,21 @@
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKCommonPipelineLayout.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_win32.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_unix.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_macos.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_android.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_core.h">
<Filter>vkutils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="vkutils">

View file

@ -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<u32> 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<u32> reply = interface.call("Inhibit", "rpcs3", "Game running");
if (reply.isValid())
{
s_dbus_cookie = reply.value();
}
break;
}
break;
}
}
#endif

View file

@ -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);

View file

@ -60,6 +60,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\Utilities\cheat_info.cpp" />
<ClCompile Include="..\Utilities\date_time.cpp" />
<ClCompile Include="..\Utilities\stack_trace.cpp" />
<ClCompile Include="Crypto\decrypt_binaries.cpp" />
<ClCompile Include="Crypto\unzip.cpp" />
@ -111,9 +112,11 @@
<ClCompile Include="Emu\NP\upnp_handler.cpp" />
<ClCompile Include="Emu\perf_monitor.cpp" />
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp" />
<ClCompile Include="Emu\RSX\Common\texture_cache_types.cpp" />
<ClCompile Include="Emu\RSX\Core\RSXContext.cpp" />
<ClCompile Include="Emu\RSX\Core\RSXDisplay.cpp" />
<ClCompile Include="Emu\RSX\Core\RSXDrawCommands.cpp" />
<ClCompile Include="Emu\RSX\GSFrameBase.cpp" />
<ClCompile Include="Emu\RSX\Host\MM.cpp" />
<ClCompile Include="Emu\RSX\Host\RSXDMAWriter.cpp" />
<ClCompile Include="Emu\RSX\NV47\FW\draw_call.cpp" />
@ -178,6 +181,7 @@
<ClCompile Include="Emu\NP\ip_address.cpp" />
<ClCompile Include="Emu\vfs_config.cpp" />
<ClCompile Include="Loader\disc.cpp" />
<ClCompile Include="util\emu_utils.cpp" />
<ClCompile Include="util\serialization_ext.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>

View file

@ -1342,6 +1342,18 @@
<ClCompile Include="Emu\RSX\Program\FragmentProgramRegister.cpp">
<Filter>Emu\GPU\RSX\Program</Filter>
</ClCompile>
<ClCompile Include="util\emu_utils.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="..\Utilities\date_time.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\GSFrameBase.cpp">
<Filter>Emu\GPU\RSX\Game Window</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\Common\texture_cache_types.cpp">
<Filter>Emu\GPU\RSX\Common</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">

View file

@ -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));
}

View file

@ -511,13 +511,6 @@ void log_q_debug(QtMsgType type, const QMessageLogContext& context, const QStrin
}
}
template <>
void fmt_class_string<std::chrono::sys_time<typename std::chrono::system_clock::duration>>::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

View file

@ -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())

View file

@ -757,6 +757,7 @@
<ClCompile Include="rpcs3qt\breakpoint_list.cpp" />
<ClCompile Include="rpcs3qt\call_stack_list.cpp" />
<ClCompile Include="rpcs3qt\camera_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\permissions.cpp" />
<ClCompile Include="rpcs3qt\ps_move_tracker_dialog.cpp" />
<ClCompile Include="rpcs3qt\cheat_manager.cpp" />
<ClCompile Include="rpcs3qt\config_adapter.cpp" />
@ -1462,6 +1463,7 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\pad_device_info.h" />
<ClInclude Include="rpcs3qt\permissions.h" />
<ClInclude Include="rpcs3qt\progress_indicator.h" />
<CustomBuild Include="rpcs3qt\qt_music_handler.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View file

@ -1161,6 +1161,9 @@
<ClCompile Include="Input\ps_move_calibration.cpp">
<Filter>Io\Move</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\permissions.cpp">
<Filter>Gui\utils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1364,6 +1367,9 @@
<ClInclude Include="Input\ps_move_calibration.h">
<Filter>Io\Move</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\permissions.h">
<Filter>Gui\utils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">

View file

@ -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;
}

View file

@ -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

View file

@ -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 <QCameraDevice>
@ -8,10 +9,6 @@
#include <QMessageBox>
#include <QPushButton>
#if QT_CONFIG(permissions)
#include <QPermissions>
#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<QCameraFormat>() && ui->combo_camera->currentData().canConvert<QCameraDevice>())
{

View file

@ -29,29 +29,6 @@ LOG_CHANNEL(log_cheat, "Cheat");
cheat_manager_dialog* cheat_manager_dialog::inst = nullptr;
template <>
void fmt_class_string<cheat_type>::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;

View file

@ -44,41 +44,9 @@ extern atomic_t<bool> g_debugger_pause_all_threads_on_bp;
extern const ppu_decoder<ppu_itype> 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<CPUDisAsm> make_disasm(const cpu_thread* cpu, shared_ptr<cpu_thread> handle)
{
if (!handle)
{
switch (cpu->get_class())
{
case thread_class::ppu: handle = idm::get_unlocked<named_thread<ppu_thread>>(cpu->id); break;
case thread_class::spu: handle = idm::get_unlocked<named_thread<spu_thread>>(cpu->id); break;
default: break;
}
}
std::shared_ptr<CPUDisAsm> result;
switch (cpu->get_class())
{
case thread_class::ppu: result = std::make_shared<PPUDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr); break;
case thread_class::spu: result = std::make_shared<SPUDisAsm>(cpu_disasm_mode::interpreter, static_cast<const spu_thread*>(cpu)->ls); break;
case thread_class::rsx: result = std::make_shared<RSXDisAsm>(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<CPUDisAsm> make_disasm(const cpu_thread* cpu, shared_ptr<cpu_thread> handle);
debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidget *parent)
: custom_dock_widget(tr("Debugger [Press F1 for Help]"), parent)

View file

@ -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";

View file

@ -53,20 +53,14 @@ extern atomic_t<bool> g_user_asked_for_recording;
extern atomic_t<bool> g_user_asked_for_screenshot;
extern atomic_t<bool> g_user_asked_for_frame_capture;
extern atomic_t<bool> g_disable_frame_limit;
extern atomic_t<bool> g_game_window_focused;
extern atomic_t<recording_mode> g_recording_mode;
atomic_t<bool> g_game_window_focused = false;
namespace pad
{
extern atomic_t<bool> 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> gui_settings, bool force_fullscreen)
: QWindow()
, m_initial_geometry(geometry)

View file

@ -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));
}

View file

@ -86,11 +86,6 @@
#include <QEventLoop>
#include <QTimer>
#if QT_CONFIG(permissions)
#include <QGuiApplication>
#include <QPermissions>
#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<bool()> 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> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> 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("<br /><br />For information on setting up the emulator and dumping your PS3 games, read the <a %0 href=\"https://rpcs3.net/quickstart\">quickstart guide</a>.").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();
}

View file

@ -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;
}

View file

@ -0,0 +1,66 @@
#include "stdafx.h"
#include "permissions.h"
#include <qglobal.h>
#if QT_CONFIG(permissions)
#include <QApplication>
#include <QPermissions>
#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<void()> repeat_callback, std::function<void()> 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<QObject*>(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;
}
}
}

Some files were not shown because too many files have changed in this diff Show more