mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 03:25:16 +00:00
Merge branch 'master' into spu-giga-crash-fix
This commit is contained in:
commit
a445443d7d
116 changed files with 2932 additions and 2035 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
76
.cirrus.yml
76
.cirrus.yml
|
@ -7,7 +7,7 @@ env:
|
|||
BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH
|
||||
RPCS3_TOKEN: ENCRYPTED[100ebb8e3552bf2021d0ef55dccda3e58d27be5b6cab0b0b92843ef490195d3c4edaefa087e4a3b425caa6392300b9b1]
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.8.1'
|
||||
QT_VER: '6.8.2'
|
||||
|
||||
# windows_task:
|
||||
# matrix:
|
||||
|
@ -21,7 +21,7 @@ env:
|
|||
# COMPILER: msvc
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\
|
||||
# QT_VER_MSVC: 'msvc2022'
|
||||
# QT_DATE: '202411221531'
|
||||
# QT_DATE: '202501260838'
|
||||
# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64
|
||||
# VULKAN_VER: '1.3.268.0'
|
||||
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
|
@ -56,7 +56,7 @@ env:
|
|||
|
||||
# linux_task:
|
||||
# container:
|
||||
# image: rpcs3/rpcs3-ci-jammy:1.0
|
||||
# image: rpcs3/rpcs3-ci-jammy:1.1
|
||||
# cpu: 4
|
||||
# memory: 16G
|
||||
# env:
|
||||
|
@ -114,38 +114,38 @@ freebsd_task:
|
|||
install_script: "sh -ex ./.ci/install-freebsd.sh"
|
||||
script: "./.ci/build-freebsd.sh"
|
||||
|
||||
linux_aarch64_task:
|
||||
env:
|
||||
BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
|
||||
ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/
|
||||
CCACHE_DIR: "/tmp/ccache_dir"
|
||||
CCACHE_MAXSIZE: 300M
|
||||
CI_HAS_ARTIFACTS: true
|
||||
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
|
||||
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
|
||||
DEPLOY_APPIMAGE: true
|
||||
APPDIR: "./appdir"
|
||||
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
COMPILER: clang
|
||||
ccache_cache:
|
||||
folder: "/tmp/ccache_dir"
|
||||
matrix:
|
||||
- name: Cirrus Linux AArch64 Clang
|
||||
arm_container:
|
||||
image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.0'
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
clang_script:
|
||||
- mkdir artifacts
|
||||
- "sh -ex ./.ci/build-linux-aarch64.sh"
|
||||
artifacts:
|
||||
name: Artifact
|
||||
path: "artifacts/*"
|
||||
push_script: |
|
||||
if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
export AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
.ci/github-upload.sh
|
||||
fi;
|
||||
# linux_aarch64_task:
|
||||
# env:
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
|
||||
# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/
|
||||
# CCACHE_DIR: "/tmp/ccache_dir"
|
||||
# CCACHE_MAXSIZE: 300M
|
||||
# CI_HAS_ARTIFACTS: true
|
||||
# UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
|
||||
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
|
||||
# DEPLOY_APPIMAGE: true
|
||||
# APPDIR: "./appdir"
|
||||
# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
# COMPILER: clang
|
||||
# ccache_cache:
|
||||
# folder: "/tmp/ccache_dir"
|
||||
# matrix:
|
||||
# - name: Cirrus Linux AArch64 Clang
|
||||
# arm_container:
|
||||
# image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.1'
|
||||
# cpu: 8
|
||||
# memory: 8G
|
||||
# clang_script:
|
||||
# - mkdir artifacts
|
||||
# - "sh -ex ./.ci/build-linux-aarch64.sh"
|
||||
# artifacts:
|
||||
# name: Artifact
|
||||
# path: "artifacts/*"
|
||||
# push_script: |
|
||||
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
|
||||
# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
# COMM_COUNT=$(git rev-list --count HEAD)
|
||||
# COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
# export AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
# .ci/github-upload.sh
|
||||
# fi;
|
||||
|
|
79
.github/workflows/rpcs3.yml
vendored
79
.github/workflows/rpcs3.yml
vendored
|
@ -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
2
3rdparty/curl/curl
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 34cf9d54a46598c44938aa7598820484d7af7133
|
||||
Subproject commit 57495c64871d18905a0941db9196ef90bafe9a29
|
2
3rdparty/libpng/libpng
vendored
2
3rdparty/libpng/libpng
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 0024abd279d3a06435c0309a3f4172eed7c7a19a
|
||||
Subproject commit 872555f4ba910252783af1507f9e7fe1653be252
|
2
3rdparty/libpng/libpng.vcxproj
vendored
2
3rdparty/libpng/libpng.vcxproj
vendored
|
@ -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>
|
||||
|
|
2
3rdparty/libsdl-org/SDL
vendored
2
3rdparty/libsdl-org/SDL
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 8236e01a9f758d15927624925c6043f84d8a261f
|
||||
Subproject commit 7a44b1ab002cee6efa56d3b4c0e146b7fbaed80b
|
2
3rdparty/opencv/CMakeLists.txt
vendored
2
3rdparty/opencv/CMakeLists.txt
vendored
|
@ -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")
|
||||
|
|
2
3rdparty/zlib/zlib.vcxproj
vendored
2
3rdparty/zlib/zlib.vcxproj
vendored
|
@ -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
2
3rdparty/zstd/zstd
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 97291fc5020a8994019ab76cf0cda83a9824374c
|
||||
Subproject commit f8745da6ff1ad1e7bab384bd1f9d742439278e99
|
19
3rdparty/zstd/zstd.vcxproj
vendored
19
3rdparty/zstd/zstd.vcxproj
vendored
|
@ -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" />
|
||||
|
|
10
BUILDING.md
10
BUILDING.md
|
@ -19,26 +19,26 @@ The following tools are required to build RPCS3 on Windows 10 or later:
|
|||
with standalone **CMake** tool.
|
||||
|
||||
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
|
||||
- [Qt 6.8.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
|
||||
- [Qt 6.8.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
|
||||
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
|
||||
|
||||
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
|
||||
|
||||
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
|
||||
- add and set the `QTDIR` environment variable, e.g. `<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**)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
11
Utilities/date_time.cpp
Normal 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);
|
||||
}
|
|
@ -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
|
||||
|
|
8
buildfiles/msvc/ci_no_debug_info.targets
Normal file
8
buildfiles/msvc/ci_no_debug_info.targets
Normal 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>
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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/");
|
||||
|
||||
|
|
|
@ -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*/)
|
||||
{
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(); };
|
||||
};
|
||||
|
|
|
@ -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*
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
37
rpcs3/Emu/RSX/Common/texture_cache_types.cpp
Normal file
37
rpcs3/Emu/RSX/Common/texture_cache_types.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
10
rpcs3/Emu/RSX/GSFrameBase.cpp
Normal file
10
rpcs3/Emu/RSX/GSFrameBase.cpp
Normal 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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "device.h"
|
||||
#include "instance.hpp"
|
||||
#include "instance.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
|
|
366
rpcs3/Emu/RSX/VK/vkutils/instance.cpp
Normal file
366
rpcs3/Emu/RSX/VK/vkutils/instance.cpp
Normal 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);
|
||||
}
|
||||
}
|
64
rpcs3/Emu/RSX/VK/vkutils/instance.h
Normal file
64
rpcs3/Emu/RSX/VK/vkutils/instance.h
Normal 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);
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
354
rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp
Normal file
354
rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp
Normal 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, ©Region);
|
||||
change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
|
||||
u32 swapchain_image_RPCS3::get_required_memory_size() const
|
||||
{
|
||||
return m_width * m_height * 4;
|
||||
}
|
||||
|
||||
void* swapchain_image_RPCS3::get_pixels()
|
||||
{
|
||||
return m_dma_buffer->map(0, VK_WHOLE_SIZE);
|
||||
}
|
||||
|
||||
void swapchain_image_RPCS3::free_pixels()
|
||||
{
|
||||
m_dma_buffer->unmap();
|
||||
}
|
||||
|
||||
// swapchain BASE
|
||||
swapchain_base::swapchain_base(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format)
|
||||
{
|
||||
dev.create(gpu, graphics_queue, present_queue, transfer_queue);
|
||||
m_surface_format = format;
|
||||
}
|
||||
|
||||
// NATIVE swapchain base
|
||||
VkResult native_swapchain_base::acquire_next_swapchain_image(VkSemaphore /*semaphore*/, u64 /*timeout*/, u32* result)
|
||||
{
|
||||
u32 index = 0;
|
||||
for (auto& p : swapchain_images)
|
||||
{
|
||||
if (!p.first)
|
||||
{
|
||||
p.first = true;
|
||||
*result = index;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return VK_NOT_READY;
|
||||
}
|
||||
|
||||
void native_swapchain_base::init_swapchain_images(render_device& dev, u32 preferred_count)
|
||||
{
|
||||
swapchain_images.resize(preferred_count);
|
||||
for (auto& img : swapchain_images)
|
||||
{
|
||||
img.second = std::make_unique<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 = ℑ
|
||||
|
||||
if (semaphore != VK_NULL_HANDLE)
|
||||
{
|
||||
present.waitSemaphoreCount = 1;
|
||||
present.pWaitSemaphores = &semaphore;
|
||||
}
|
||||
|
||||
return _vkQueuePresentKHR(dev.get_present_queue(), &present);
|
||||
}
|
||||
}
|
11
rpcs3/Emu/RSX/VK/vkutils/swapchain.h
Normal file
11
rpcs3/Emu/RSX/VK/vkutils/swapchain.h
Normal 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
|
|
@ -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, ©Region);
|
||||
change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
|
||||
u32 get_required_memory_size() const
|
||||
{
|
||||
return m_width * m_height * 4;
|
||||
}
|
||||
|
||||
void* get_pixels()
|
||||
{
|
||||
return m_dma_buffer->map(0, VK_WHOLE_SIZE);
|
||||
}
|
||||
|
||||
void free_pixels()
|
||||
{
|
||||
m_dma_buffer->unmap();
|
||||
}
|
||||
};
|
||||
|
||||
class swapchain_base
|
||||
{
|
||||
protected:
|
||||
render_device dev;
|
||||
|
||||
display_handle_t window_handle{};
|
||||
u32 m_width = 0;
|
||||
u32 m_height = 0;
|
||||
VkFormat m_surface_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
virtual void init_swapchain_images(render_device& dev, u32 count) = 0;
|
||||
|
||||
public:
|
||||
swapchain_base(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM)
|
||||
{
|
||||
dev.create(gpu, graphics_queue, present_queue, transfer_queue);
|
||||
m_surface_format = format;
|
||||
}
|
||||
|
||||
virtual ~swapchain_base() = default;
|
||||
|
||||
virtual void create(display_handle_t& handle) = 0;
|
||||
virtual void destroy(bool full = true) = 0;
|
||||
virtual bool init() = 0;
|
||||
|
||||
virtual u32 get_swap_image_count() const = 0;
|
||||
virtual VkImage get_image(u32 index) = 0;
|
||||
virtual VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) = 0;
|
||||
virtual void end_frame(command_buffer& cmd, u32 index) = 0;
|
||||
virtual VkResult present(VkSemaphore semaphore, u32 index) = 0;
|
||||
virtual VkImageLayout get_optimal_present_layout() = 0;
|
||||
|
||||
virtual bool supports_automatic_wm_reports() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool init(u32 w, u32 h)
|
||||
{
|
||||
m_width = w;
|
||||
m_height = h;
|
||||
return init();
|
||||
}
|
||||
|
||||
const vk::render_device& get_device()
|
||||
{
|
||||
return dev;
|
||||
}
|
||||
|
||||
VkFormat get_surface_format()
|
||||
{
|
||||
return m_surface_format;
|
||||
}
|
||||
|
||||
bool is_headless() const
|
||||
{
|
||||
return (dev.get_present_queue() == VK_NULL_HANDLE);
|
||||
}
|
||||
};
|
||||
|
||||
template<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 = ℑ
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
19
rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp
Normal file
19
rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp
Normal 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
|
||||
}
|
||||
|
227
rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h
Normal file
227
rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h
Normal 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;
|
||||
};
|
||||
}
|
23
rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp
Normal file
23
rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp
Normal 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
|
||||
}
|
172
rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp
Normal file
172
rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp
Normal 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");
|
||||
}
|
||||
}
|
110
rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp
Normal file
110
rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp
Normal 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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>())
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
66
rpcs3/rpcs3qt/permissions.cpp
Normal file
66
rpcs3/rpcs3qt/permissions.cpp
Normal 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
Loading…
Add table
Reference in a new issue