Compare commits
227 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67dfc7916c | ||
|
|
8d15388aeb | ||
|
|
cdae6a4ce5 | ||
|
|
4da21a016e | ||
|
|
8b139ff5fa | ||
|
|
1620481331 | ||
|
|
7794fc3ce7 | ||
|
|
97bb22998b | ||
|
|
c59f6fd5c3 | ||
|
|
8b2c151f1f | ||
|
|
9d6eee6acf | ||
|
|
a8d2684929 | ||
|
|
6e00121eb5 | ||
|
|
406041b7ad | ||
|
|
be1d11eb62 | ||
|
|
7654a08d9a | ||
|
|
8e08756b6e | ||
|
|
5a071f3137 | ||
|
|
e596e9350b | ||
|
|
0ecb6695de | ||
|
|
c38e3f77f9 | ||
|
|
87f8fea4de | ||
|
|
47ba6c6344 | ||
|
|
a13d1d1ab1 | ||
|
|
24bce59f70 | ||
|
|
96ea686eb6 | ||
|
|
9814a1b788 | ||
|
|
ddc35639a8 | ||
|
|
181b005636 | ||
|
|
ac6b4a625d | ||
|
|
88c37048ac | ||
|
|
aa958c4d19 | ||
|
|
b4ced58acc | ||
|
|
f79b7f10ab | ||
|
|
0f0d9740c2 | ||
|
|
70430313e3 | ||
|
|
b62d2f839d | ||
|
|
25de4d6b65 | ||
|
|
877cda9b9a | ||
|
|
170db22d9c | ||
|
|
437ebc1e02 | ||
|
|
6fc7b3993d | ||
|
|
08343faf01 | ||
|
|
a588bc5da8 | ||
|
|
29ad2eca62 | ||
|
|
168ba8dbcd | ||
|
|
7e27afb37d | ||
|
|
09725bd921 | ||
|
|
a3df2448ec | ||
|
|
9b54e82314 | ||
|
|
b965b094b7 | ||
|
|
bd9f82df94 | ||
|
|
f0ee3919e0 | ||
|
|
cf2e617f08 | ||
|
|
8776eba8c8 | ||
|
|
1cc4ab7e88 | ||
|
|
d3491bfced | ||
|
|
1e16fe5349 | ||
|
|
97b5289f15 | ||
|
|
7c00ac637a | ||
|
|
b412cb4cca | ||
|
|
87a76af86f | ||
|
|
cd085ea2de | ||
|
|
169b641bd1 | ||
|
|
dbdbb40c0b | ||
|
|
dc99d3ebfc | ||
|
|
04ad430115 | ||
|
|
66f1bb937f | ||
|
|
0f91661660 | ||
|
|
2f80d7565d | ||
|
|
21eb175aa1 | ||
|
|
dcc4057dd8 | ||
|
|
0df0d0cb66 | ||
|
|
0a5f46942e | ||
|
|
09cbccb40b | ||
|
|
d91ad6174e | ||
|
|
fd4893f6ef | ||
|
|
6e986f8133 | ||
|
|
3982ef7188 | ||
|
|
0e0de5a2a0 | ||
|
|
ab6901ae6a | ||
|
|
87f8f3a59e | ||
|
|
299a29e243 | ||
|
|
0a12ba4120 | ||
|
|
100036aecf | ||
|
|
56e8ed7833 | ||
|
|
c9f894c45a | ||
|
|
873fbc469b | ||
|
|
ddb0928f10 | ||
|
|
d4eae28ce2 | ||
|
|
6fe26173dc | ||
|
|
7d4f1ce5f9 | ||
|
|
e45eb0da9a | ||
|
|
53b7c5cc77 | ||
|
|
4fa55b7aa2 | ||
|
|
3e7137cc6b | ||
|
|
aba803bd04 | ||
|
|
20915eb5b8 | ||
|
|
f139762c64 | ||
|
|
96344873d6 | ||
|
|
01fb320f4e | ||
|
|
ce53c41205 | ||
|
|
7389cf3e89 | ||
|
|
20db37f235 | ||
|
|
d7e5b5f13f | ||
|
|
75c92a7cd1 | ||
|
|
3c0255b953 | ||
|
|
310814ac71 | ||
|
|
4ce95e55e0 | ||
|
|
351e4861ca | ||
|
|
2fd4861d3e | ||
|
|
60092ce849 | ||
|
|
2a7d56dbf2 | ||
|
|
927bb0c175 | ||
|
|
5bb45dc7ba | ||
|
|
8576d5e72c | ||
|
|
ee57c2fd69 | ||
|
|
1dabea7514 | ||
|
|
2b8c2ce423 | ||
|
|
a5968b630d | ||
|
|
76644a0169 | ||
|
|
ec6579cb4f | ||
|
|
9f79764b01 | ||
|
|
be411b37d4 | ||
|
|
49ceff71a2 | ||
|
|
da519f9091 | ||
|
|
af398e3684 | ||
|
|
009f956d8d | ||
|
|
ff13aff862 | ||
|
|
54dafce541 | ||
|
|
5e26294e27 | ||
|
|
ed24632ceb | ||
|
|
7209b7d786 | ||
|
|
1a34c2a189 | ||
|
|
388d717205 | ||
|
|
7e533ccf50 | ||
|
|
93317911eb | ||
|
|
394b7fa671 | ||
|
|
d20efcb0d2 | ||
|
|
ee1e55d5e1 | ||
|
|
61f750bdd9 | ||
|
|
e68774d449 | ||
|
|
75adf7c8d1 | ||
|
|
65f72372f0 | ||
|
|
f93b8c1e8d | ||
|
|
7d96c9d634 | ||
|
|
3dea1a9f81 | ||
|
|
3a36615da7 | ||
|
|
b92dc8c714 | ||
|
|
e4c8626806 | ||
|
|
bf3e43b016 | ||
|
|
82c7c6aed1 | ||
|
|
98ea06e82d | ||
|
|
7084fc4c41 | ||
|
|
0be0f18764 | ||
|
|
dda5cc411f | ||
|
|
348da93ee6 | ||
|
|
cda2317ddb | ||
|
|
afe32d1e03 | ||
|
|
603f709784 | ||
|
|
fc6c755e5a | ||
|
|
62c59d195f | ||
|
|
cbbf3505e7 | ||
|
|
c7ff0419ad | ||
|
|
398019867b | ||
|
|
d9f287eaa2 | ||
|
|
80bf46da4c | ||
|
|
5e98a3e1d8 | ||
|
|
40d00e3066 | ||
|
|
a4168150ed | ||
|
|
545e94d267 | ||
|
|
36271b9cdf | ||
|
|
dc96338c2e | ||
|
|
7b5d66e5c1 | ||
|
|
1dd2e46fce | ||
|
|
bc6c0de76d | ||
|
|
4e4f3d7504 | ||
|
|
65bd62e98b | ||
|
|
7476287649 | ||
|
|
a5298c4025 | ||
|
|
651ab4c62c | ||
|
|
687be5b132 | ||
|
|
75166ecd2a | ||
|
|
938b9b9826 | ||
|
|
ebebafed64 | ||
|
|
cf342e7a4b | ||
|
|
50fc5e339d | ||
|
|
c73fad6772 | ||
|
|
eef0e6fc63 | ||
|
|
23f4b304b8 | ||
|
|
0b7126e3be | ||
|
|
675fd4b31a | ||
|
|
b7e2903911 | ||
|
|
1d23f40e02 | ||
|
|
6295d6c416 | ||
|
|
54e2179337 | ||
|
|
ddb82a690b | ||
|
|
3d5a6203e1 | ||
|
|
e67c10de01 | ||
|
|
6a70d08043 | ||
|
|
5a4d5b9f31 | ||
|
|
45e206e248 | ||
|
|
11c155d0f1 | ||
|
|
f657ab3cc6 | ||
|
|
a984d44fd3 | ||
|
|
e634461ebb | ||
|
|
1ec8f34a99 | ||
|
|
b2de662d67 | ||
|
|
36ef61908d | ||
|
|
cc99f52606 | ||
|
|
f79da986e3 | ||
|
|
cec9275c85 | ||
|
|
19cb4ec05d | ||
|
|
3f12fb0c91 | ||
|
|
beb809b612 | ||
|
|
6da67645e9 | ||
|
|
23bf8bf5e7 | ||
|
|
7f9bc0abbd | ||
|
|
1620eea37b | ||
|
|
f97f73f0b5 | ||
|
|
43e7c00fdd | ||
|
|
4ba19a02b0 | ||
|
|
ad9f1370d5 | ||
|
|
8c8a6ccddd | ||
|
|
5799091044 | ||
|
|
5a8e8f5936 | ||
|
|
a016792371 |
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
ko_fi: shadps4
|
||||
7
.github/linux-appimage-qt.sh
vendored
@@ -9,6 +9,8 @@ fi
|
||||
|
||||
export Qt6_DIR="/usr/lib/qt6"
|
||||
export PATH="$Qt6_DIR/bin:$PATH"
|
||||
export EXTRA_QT_PLUGINS="waylandcompositor"
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
|
||||
# Prepare Tools for building the AppImage
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
@@ -19,12 +21,13 @@ chmod a+x linuxdeploy-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh
|
||||
|
||||
|
||||
# Build AppImage
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir
|
||||
./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir
|
||||
|
||||
cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin
|
||||
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt --output appimage
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt
|
||||
rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage
|
||||
mv Shadps4-x86_64.AppImage Shadps4-qt.AppImage
|
||||
|
||||
537
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,537 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Build and Release
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.event_name }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'push' }}
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
reuse:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: fsfe/reuse-action@v4
|
||||
|
||||
clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'
|
||||
sudo apt update
|
||||
sudo apt install clang-format-17
|
||||
- name: Build
|
||||
env:
|
||||
COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
|
||||
run: ./.ci/clang-format.sh
|
||||
|
||||
get-info:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
date: ${{ steps.vars.outputs.date }}
|
||||
shorthash: ${{ steps.vars.outputs.shorthash }}
|
||||
fullhash: ${{ steps.vars.outputs.fullhash }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
run: |
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
windows-sdl:
|
||||
runs-on: windows-latest
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-ninja-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Setup VS Environment
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: amd64
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Upload Windows SDL artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-win64-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: ${{github.workspace}}/build/shadPS4.exe
|
||||
|
||||
windows-qt:
|
||||
runs-on: windows-latest
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.7.3
|
||||
host: windows
|
||||
target: desktop
|
||||
arch: win64_msvc2019_64
|
||||
archives: qtbase qttools
|
||||
modules: qtmultimedia
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-ninja-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Setup VS Environment
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: amd64
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Deploy and Package
|
||||
run: |
|
||||
mkdir upload
|
||||
move build/shadPS4.exe upload
|
||||
windeployqt --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --dir upload upload/shadPS4.exe
|
||||
Compress-Archive -Path upload/* -DestinationPath shadps4-win64-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}.zip
|
||||
|
||||
- name: Upload Windows Qt artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-win64-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: upload/
|
||||
|
||||
macos-sdl:
|
||||
runs-on: macos-latest
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup latest Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Install MoltenVK
|
||||
run: |
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew install molten-vk
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{runner.os}}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
create-symlink: true
|
||||
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
variant: sccache
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Package and Upload macOS SDL artifact
|
||||
run: |
|
||||
mkdir upload
|
||||
mv ${{github.workspace}}/build/shadps4 upload
|
||||
cp $(arch -x86_64 /usr/local/bin/brew --prefix)/opt/molten-vk/lib/libMoltenVK.dylib upload
|
||||
tar cf shadps4-macos-sdl.tar.gz -C upload .
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: shadps4-macos-sdl.tar.gz
|
||||
|
||||
macos-qt:
|
||||
runs-on: macos-latest
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup latest Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Install MoltenVK and Setup Qt
|
||||
run: |
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew install molten-vk
|
||||
- uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.7.3
|
||||
host: mac
|
||||
target: desktop
|
||||
arch: clang_64
|
||||
archives: qtbase qttools
|
||||
modules: qtmultimedia
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{runner.os}}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
create-symlink: true
|
||||
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
variant: sccache
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Package and Upload macOS Qt artifact
|
||||
run: |
|
||||
mkdir upload
|
||||
mv ${{github.workspace}}/build/shadps4.app upload
|
||||
macdeployqt upload/shadps4.app
|
||||
tar cf shadps4-macos-qt.tar.gz -C upload .
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-macos-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: shadps4-macos-qt.tar.gz
|
||||
|
||||
linux-sdl:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Package and Upload Linux(ubuntu64) SDL artifact
|
||||
run: |
|
||||
ls -la ${{ github.workspace }}/build/shadps4
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-ubuntu64-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: ${{ github.workspace }}/build/shadps4
|
||||
|
||||
- name: Run AppImage packaging script
|
||||
run: ./.github/linux-appimage-sdl.sh
|
||||
|
||||
- name: Package and Upload Linux SDL artifact
|
||||
run: |
|
||||
tar cf shadps4-linux-sdl.tar.gz -C ${{github.workspace}}/build shadps4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-linux-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: Shadps4-sdl.AppImage
|
||||
|
||||
linux-qt:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3
|
||||
|
||||
- name: Run AppImage packaging script
|
||||
run: ./.github/linux-appimage-qt.sh
|
||||
|
||||
- name: Package and Upload Linux Qt artifact
|
||||
run: |
|
||||
tar cf shadps4-linux-qt.tar.gz -C ${{github.workspace}}/build shadps4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: Shadps4-qt.AppImage
|
||||
|
||||
pre-release:
|
||||
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
|
||||
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts
|
||||
|
||||
- name: Compress individual directories (without parent directory)
|
||||
run: |
|
||||
cd ./artifacts
|
||||
for dir in */; do
|
||||
if [ -d "$dir" ]; then
|
||||
dir_name=${dir%/}
|
||||
echo "Creating zip for $dir_name"
|
||||
(cd "$dir_name" && zip -r "../${dir_name}.zip" .)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Get latest release information
|
||||
id: get_latest_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
run: |
|
||||
api_url="https://api.github.com/repos/${{ github.repository }}"
|
||||
latest_release_info=$(curl -H "Authorization: token $GITHUB_TOKEN" "$api_url/releases/latest")
|
||||
echo "last_release_tag=$(echo "$latest_release_info" | jq -r '.tag_name')" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Pre-Release on GitHub
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
|
||||
tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
|
||||
draft: false
|
||||
prerelease: true
|
||||
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
|
||||
artifacts: ./artifacts/*.zip
|
||||
|
||||
- name: Publish to Release Repository
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
run: |
|
||||
ARTIFACTS_DIR=./artifacts
|
||||
REPO_WINDOWS="shadps4-emu/shadps4-binaries-Windows"
|
||||
REPO_LINUX="shadps4-emu/shadps4-binaries-Linux"
|
||||
REPO_MAC="shadps4-emu/shadps4-binaries-Mac"
|
||||
|
||||
for file in "$ARTIFACTS_DIR"/*.zip; do
|
||||
filename=$(basename "$file")
|
||||
REPO=""
|
||||
|
||||
# Determine repository based on file name
|
||||
if [[ "$filename" == *"win64"* ]]; then
|
||||
REPO=$REPO_WINDOWS
|
||||
elif [[ "$filename" == *"linux"* ]] || [[ "$filename" == *"ubuntu64"* ]]; then
|
||||
REPO=$REPO_LINUX
|
||||
elif [[ "$filename" == *"macos"* ]]; then
|
||||
REPO=$REPO_MAC
|
||||
fi
|
||||
|
||||
# If REPO is empty, skip file
|
||||
if [[ -z "$REPO" ]]; then
|
||||
echo "No matching repository for $filename"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if release already exists and get ID
|
||||
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
|
||||
|
||||
if [[ "$release_id" == "null" ]]; then
|
||||
echo "Creating release in $REPO for $filename"
|
||||
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d '{
|
||||
"tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
|
||||
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
|
||||
"draft": false,
|
||||
"prerelease": true,
|
||||
"body": "Commit: [${{ needs.get-info.outputs.fullhash }}](https://github.com/shadps4-emu/shadPS4/commit/${{ needs.get-info.outputs.fullhash }})"
|
||||
}' "https://api.github.com/repos/$REPO/releases" | jq -r '.id')
|
||||
else
|
||||
echo "Release already exists in $REPO with ID $release_id"
|
||||
fi
|
||||
|
||||
# Artifact upload
|
||||
echo "Uploading $filename to release $release_id in $REPO"
|
||||
upload_url="https://uploads.github.com/repos/$REPO/releases/$release_id/assets?name=$filename"
|
||||
curl -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/octet-stream" --data-binary @"$file" "$upload_url"
|
||||
done
|
||||
|
||||
- name: Get current pre-release information
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
run: |
|
||||
api_url="https://api.github.com/repos/${{ github.repository }}/releases"
|
||||
|
||||
# Get all releases (sorted by date)
|
||||
releases=$(curl -H "Authorization: token $GITHUB_TOKEN" "$api_url")
|
||||
|
||||
# Capture the most recent pre-release (assuming the first one is the latest)
|
||||
current_release=$(echo "$releases" | jq -c '.[] | select(.prerelease == true) | .published_at' | sort -r | head -n 1)
|
||||
|
||||
# Remove extra quotes from captured date
|
||||
current_release=$(echo $current_release | tr -d '"')
|
||||
|
||||
# Export the current published_at to be available for the next step
|
||||
echo "CURRENT_PUBLISHED_AT=$current_release" >> $GITHUB_ENV
|
||||
|
||||
- name: Delete old pre-releases and tags
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
run: |
|
||||
api_url="https://api.github.com/repos/${{ github.repository }}/releases"
|
||||
|
||||
# Get current pre-releases
|
||||
releases=$(curl -H "Authorization: token $GITHUB_TOKEN" "$api_url")
|
||||
|
||||
# Remove extra quotes from captured date
|
||||
CURRENT_PUBLISHED_AT=$(echo $CURRENT_PUBLISHED_AT | tr -d '"')
|
||||
|
||||
# Convert CURRENT_PUBLISHED_AT para timestamp Unix
|
||||
current_published_ts=$(date -d "$CURRENT_PUBLISHED_AT" +%s)
|
||||
|
||||
# Identify pre-releases
|
||||
echo "$releases" | jq -c '.[] | select(.prerelease == true)' | while read -r release; do
|
||||
release_date=$(echo "$release" | jq -r '.published_at')
|
||||
release_id=$(echo "$release" | jq -r '.id')
|
||||
release_tag=$(echo "$release" | jq -r '.tag_name')
|
||||
|
||||
# Remove extra quotes from captured date
|
||||
release_date=$(echo $release_date | tr -d '"')
|
||||
|
||||
# Convert release_date para timestamp Unix
|
||||
release_date_ts=$(date -d "$release_date" +%s)
|
||||
|
||||
# Compare timestamps and delete old pre-releases
|
||||
if [[ "$release_date_ts" -lt "$current_published_ts" ]]; then
|
||||
echo "Deleting old pre-release: $release_id from $release_date with tag: $release_tag"
|
||||
# Delete the pre-release
|
||||
curl -X DELETE -H "Authorization: token $GITHUB_TOKEN" "$api_url/$release_id"
|
||||
# Delete the tag
|
||||
curl -X DELETE -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$release_tag"
|
||||
else
|
||||
echo "Skipping pre-release: $release_id (newer or same date)"
|
||||
fi
|
||||
done
|
||||
17
.github/workflows/ci.yml
vendored
@@ -1,17 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Reuse
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
tags: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
jobs:
|
||||
reuse:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: fsfe/reuse-action@v4
|
||||
28
.github/workflows/format.yml
vendored
@@ -1,28 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Clang Format
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'
|
||||
sudo apt update
|
||||
sudo apt install clang-format-17
|
||||
- name: Build
|
||||
env:
|
||||
COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
|
||||
run: ./.ci/clang-format.sh
|
||||
66
.github/workflows/linux-qt.yml
vendored
@@ -1,66 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Linux-Qt
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: >
|
||||
sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Run AppImage packaging script
|
||||
run: ./.github/linux-appimage-qt.sh
|
||||
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
run: |
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-linux-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: Shadps4-qt.AppImage
|
||||
73
.github/workflows/linux.yml
vendored
@@ -1,73 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: >
|
||||
sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
run: |
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-ubuntu64-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: |
|
||||
${{github.workspace}}/build/shadps4
|
||||
|
||||
- name: Run AppImage packaging script
|
||||
run: ./.github/linux-appimage-sdl.sh
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-sdl-appimage-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: Shadps4-sdl.AppImage
|
||||
88
.github/workflows/macos-qt.yml
vendored
@@ -1,88 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: macOS-Qt
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup latest Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Install MoltenVK
|
||||
run: |
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew install molten-vk
|
||||
|
||||
- name: Setup Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.7.2
|
||||
host: mac
|
||||
target: desktop
|
||||
arch: clang_64
|
||||
archives: qtbase qttools
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{runner.os}}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
create-symlink: true
|
||||
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
variant: sccache
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
mkdir upload
|
||||
mv ${{github.workspace}}/build/shadps4.app upload
|
||||
macdeployqt upload/shadps4.app
|
||||
tar cf shadps4-macos-qt.tar.gz -C upload .
|
||||
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
run: |
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-macos-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: shadps4-macos-qt.tar.gz
|
||||
79
.github/workflows/macos.yml
vendored
@@ -1,79 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup latest Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Install MoltenVK
|
||||
run: |
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew install molten-vk
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{runner.os}}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
create-symlink: true
|
||||
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
variant: sccache
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
mkdir upload
|
||||
mv ${{github.workspace}}/build/shadps4 upload
|
||||
cp $(arch -x86_64 /usr/local/bin/brew --prefix)/opt/molten-vk/lib/libMoltenVK.dylib upload
|
||||
install_name_tool -add_rpath "@loader_path" upload/shadps4
|
||||
tar cf shadps4-macos-sdl.tar.gz -C upload .
|
||||
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
run: |
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-macos-sdl-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: shadps4-macos-sdl.tar.gz
|
||||
80
.github/workflows/windows-qt.yml
vendored
@@ -1,80 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Windows-Qt
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.7.2
|
||||
host: windows
|
||||
target: desktop
|
||||
arch: win64_msvc2019_64
|
||||
archives: qtbase qttools
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-ninja-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Setup VS Environment
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: amd64
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Deploy
|
||||
run: |
|
||||
mkdir upload
|
||||
move build/shadPS4.exe upload
|
||||
windeployqt --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --dir upload upload/shadPS4.exe
|
||||
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
shell: pwsh
|
||||
run: |
|
||||
echo "date=$(Get-Date -Format 'yyyy-MM-dd')" >> $env:GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-win64-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: upload
|
||||
65
.github/workflows/windows.yml
vendored
@@ -1,65 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-ninja-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Setup VS Environment
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: amd64
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Get date and git hash
|
||||
id: vars
|
||||
shell: pwsh
|
||||
run: |
|
||||
echo "date=$(Get-Date -Format 'yyyy-MM-dd')" >> $env:GITHUB_OUTPUT
|
||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-win64-sdl-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
|
||||
path: |
|
||||
${{github.workspace}}/build/shadPS4.exe
|
||||
2
.gitignore
vendored
@@ -387,6 +387,8 @@ FodyWeavers.xsd
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
/CMakeUserPresets.json
|
||||
/compile_commands.json
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
6
.gitmodules
vendored
@@ -85,6 +85,7 @@
|
||||
[submodule "externals/half"]
|
||||
path = externals/half
|
||||
url = https://github.com/ROCm/half.git
|
||||
shallow = true
|
||||
[submodule "externals/dear_imgui"]
|
||||
path = externals/dear_imgui
|
||||
url = https://github.com/shadps4-emu/ext-imgui.git
|
||||
@@ -93,3 +94,8 @@
|
||||
[submodule "externals/pugixml"]
|
||||
path = externals/pugixml
|
||||
url = https://github.com/zeux/pugixml.git
|
||||
shallow = true
|
||||
[submodule "externals/discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/shadps4-emu/ext-discord-rpc.git
|
||||
shallow = true
|
||||
64
.reuse/dep5
@@ -1,64 +0,0 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Comment: It is best to use this file to record copyright information about
|
||||
generated, binary and third party files
|
||||
|
||||
Files: CMakeSettings.json
|
||||
.github/shadps4.desktop
|
||||
.github/shadps4.png
|
||||
.gitmodules
|
||||
documents/changelog.txt
|
||||
documents/readme.txt
|
||||
documents/Quickstart/1.png
|
||||
documents/Quickstart/2.png
|
||||
documents/Screenshots/Bloodborne.png
|
||||
documents/Screenshots/Sonic Mania.png
|
||||
documents/Screenshots/Undertale.png
|
||||
documents/Screenshots/We are DOOMED.png
|
||||
scripts/ps4_names.txt
|
||||
src/images/about_icon.png
|
||||
src/images/controller_icon.png
|
||||
src/images/exit_icon.png
|
||||
src/images/file_icon.png
|
||||
src/images/flag_china.png
|
||||
src/images/flag_eu.png
|
||||
src/images/flag_jp.png
|
||||
src/images/flag_unk.png
|
||||
src/images/flag_us.png
|
||||
src/images/flag_world.png
|
||||
src/images/folder_icon.png
|
||||
src/images/grid_icon.png
|
||||
src/images/iconsize_icon.png
|
||||
src/images/list_icon.png
|
||||
src/images/list_mode_icon.png
|
||||
src/images/pause_icon.png
|
||||
src/images/play_icon.png
|
||||
src/images/refresh_icon.png
|
||||
src/images/settings_icon.png
|
||||
src/images/stop_icon.png
|
||||
src/images/shadPS4.icns
|
||||
src/images/shadps4.ico
|
||||
src/images/themes_icon.png
|
||||
src/shadps4.qrc
|
||||
src/shadps4.rc
|
||||
Copyright: shadPS4 Emulator Project
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
Files: externals/cmake-modules/*
|
||||
Copyright: 2009-2010 Iowa State University
|
||||
License: BSL-1.0
|
||||
|
||||
Files: externals/renderdoc/*
|
||||
Copyright: 2019-2024 Baldur Karlsson
|
||||
License: MIT
|
||||
|
||||
Files: externals/stb_image.h
|
||||
Copyright: 2017 Sean Barrett
|
||||
License: MIT
|
||||
|
||||
Files: externals/tracy/*
|
||||
Copyright: 2017-2024 Bartosz Taudul <wolf@nereid.pl>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: src/imgui/renderer/fonts/NotoSansJP-Regular.ttf
|
||||
Copyright: 2012 Google Inc. All Rights Reserved.
|
||||
License: OFL-1.1
|
||||
123
CMakeLists.txt
Normal file → Executable file
@@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
if(APPLE)
|
||||
enable_language(OBJC)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 11)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 14)
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
@@ -30,6 +30,7 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
|
||||
option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON)
|
||||
|
||||
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
|
||||
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
@@ -47,9 +48,15 @@ else()
|
||||
message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64")
|
||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
# Exclude ARM homebrew path to avoid conflicts when cross compiling.
|
||||
list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew")
|
||||
|
||||
# Need to reconfigure pkg-config to use the right architecture library paths.
|
||||
# It's not ideal to override these but otherwise the build breaks just by having pkg-config installed.
|
||||
set(ENV{PKG_CONFIG_DIR} "")
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig:${CMAKE_SYSROOT}/usr/local/lib/pkgconfig:${CMAKE_SYSROOT}/usr/local/share/pkgconfig")
|
||||
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
|
||||
endif()
|
||||
|
||||
# This function should be passed a list of all files in a target. It will automatically generate file groups
|
||||
@@ -95,6 +102,7 @@ include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp" @ONLY)
|
||||
|
||||
@@ -102,6 +110,7 @@ find_package(Boost 1.84.0 CONFIG)
|
||||
find_package(FFmpeg 5.1.2 MODULE)
|
||||
find_package(fmt 10.2.0 CONFIG)
|
||||
find_package(glslang 14.2.0 CONFIG)
|
||||
find_package(half 1.12.0 MODULE)
|
||||
find_package(magic_enum 0.9.6 CONFIG)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
find_package(SDL3 3.1.2 CONFIG)
|
||||
@@ -143,7 +152,7 @@ add_subdirectory(externals)
|
||||
include_directories(src)
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network Multimedia)
|
||||
qt_standard_project_setup()
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
@@ -281,6 +290,12 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
||||
src/core/libraries/audio3d/audio3d_error.h
|
||||
src/core/libraries/audio3d/audio3d_impl.cpp
|
||||
src/core/libraries/audio3d/audio3d_impl.h
|
||||
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
|
||||
src/core/libraries/game_live_streaming/gamelivestreaming.h
|
||||
src/core/libraries/remote_play/remoteplay.cpp
|
||||
src/core/libraries/remote_play/remoteplay.h
|
||||
src/core/libraries/share_play/shareplay.cpp
|
||||
src/core/libraries/share_play/shareplay.h
|
||||
)
|
||||
|
||||
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
|
||||
@@ -294,11 +309,17 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp
|
||||
src/core/libraries/libc_internal/libc_internal.h
|
||||
)
|
||||
|
||||
set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp
|
||||
src/core/libraries/dialogs/error_dialog.h
|
||||
src/core/libraries/dialogs/ime_dialog.cpp
|
||||
src/core/libraries/dialogs/ime_dialog.h
|
||||
src/core/libraries/dialogs/error_codes.h
|
||||
set(IME_LIB src/core/libraries/ime/error_dialog.cpp
|
||||
src/core/libraries/ime/error_dialog.h
|
||||
src/core/libraries/ime/ime_common.h
|
||||
src/core/libraries/ime/ime_dialog_ui.cpp
|
||||
src/core/libraries/ime/ime_dialog_ui.h
|
||||
src/core/libraries/ime/ime_dialog.cpp
|
||||
src/core/libraries/ime/ime_dialog.h
|
||||
src/core/libraries/ime/ime_ui.cpp
|
||||
src/core/libraries/ime/ime_ui.h
|
||||
src/core/libraries/ime/ime.cpp
|
||||
src/core/libraries/ime/ime.h
|
||||
)
|
||||
|
||||
set(PAD_LIB src/core/libraries/pad/pad.cpp
|
||||
@@ -323,6 +344,17 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp
|
||||
src/core/libraries/usbd/usbd.h
|
||||
)
|
||||
|
||||
set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
|
||||
src/core/libraries/fiber/fiber.h
|
||||
)
|
||||
|
||||
set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
|
||||
src/core/libraries/videodec/videodec2_impl.h
|
||||
src/core/libraries/videodec/videodec2.cpp
|
||||
src/core/libraries/videodec/videodec2.h
|
||||
src/core/libraries/videodec/videodec2_avc.h
|
||||
)
|
||||
|
||||
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
|
||||
src/core/libraries/np_manager/np_manager.h
|
||||
src/core/libraries/np_score/np_score.cpp
|
||||
@@ -337,6 +369,29 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
|
||||
src/core/libraries/screenshot/screenshot.h
|
||||
)
|
||||
|
||||
set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||
src/core/devtools/layer.h
|
||||
src/core/devtools/options.cpp
|
||||
src/core/devtools/options.h
|
||||
src/core/devtools/gcn/gcn_context_regs.cpp
|
||||
src/core/devtools/gcn/gcn_op_names.cpp
|
||||
src/core/devtools/gcn/gcn_shader_regs.cpp
|
||||
src/core/devtools/widget/cmd_list.cpp
|
||||
src/core/devtools/widget/cmd_list.h
|
||||
src/core/devtools/widget/common.h
|
||||
src/core/devtools/widget/frame_dump.cpp
|
||||
src/core/devtools/widget/frame_dump.h
|
||||
src/core/devtools/widget/frame_graph.cpp
|
||||
src/core/devtools/widget/frame_graph.h
|
||||
src/core/devtools/widget/imgui_memory_editor.h
|
||||
src/core/devtools/widget/reg_popup.cpp
|
||||
src/core/devtools/widget/reg_popup.h
|
||||
src/core/devtools/widget/reg_view.cpp
|
||||
src/core/devtools/widget/reg_view.h
|
||||
src/core/devtools/widget/text_editor.cpp
|
||||
src/core/devtools/widget/text_editor.h
|
||||
)
|
||||
|
||||
set(COMMON src/common/logging/backend.cpp
|
||||
src/common/logging/backend.h
|
||||
src/common/logging/filter.cpp
|
||||
@@ -358,8 +413,8 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/config.h
|
||||
src/common/cstring.h
|
||||
src/common/debug.h
|
||||
src/common/disassembler.cpp
|
||||
src/common/disassembler.h
|
||||
src/common/decoder.cpp
|
||||
src/common/decoder.h
|
||||
src/common/elf_info.h
|
||||
src/common/endian.h
|
||||
src/common/enum.h
|
||||
@@ -378,6 +433,8 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/polyfill_thread.h
|
||||
src/common/rdtsc.cpp
|
||||
src/common/rdtsc.h
|
||||
src/common/signal_context.h
|
||||
src/common/signal_context.cpp
|
||||
src/common/singleton.h
|
||||
src/common/slot_vector.h
|
||||
src/common/string_util.cpp
|
||||
@@ -390,12 +447,18 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/version.h
|
||||
src/common/ntapi.h
|
||||
src/common/ntapi.cpp
|
||||
src/common/number_utils.h
|
||||
src/common/number_utils.cpp
|
||||
src/common/memory_patcher.h
|
||||
src/common/memory_patcher.cpp
|
||||
src/common/scm_rev.cpp
|
||||
src/common/scm_rev.h
|
||||
)
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
list(APPEND COMMON src/common/discord_rpc_handler.cpp src/common/discord_rpc_handler.h)
|
||||
endif()
|
||||
|
||||
set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/aerolib/stubs.h
|
||||
src/core/aerolib/aerolib.cpp
|
||||
@@ -445,7 +508,12 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
${RANDOM_LIB}
|
||||
${USBD_LIB}
|
||||
${MISC_LIBS}
|
||||
${DIALOGS_LIB}
|
||||
${IME_LIB}
|
||||
${FIBER_LIB}
|
||||
${VDEC_LIB}
|
||||
${DEV_TOOLS}
|
||||
src/core/debug_state.cpp
|
||||
src/core/debug_state.h
|
||||
src/core/linker.cpp
|
||||
src/core/linker.h
|
||||
src/core/memory.cpp
|
||||
@@ -499,6 +567,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/frontend/translate/data_share.cpp
|
||||
src/shader_recompiler/frontend/translate/export.cpp
|
||||
src/shader_recompiler/frontend/translate/scalar_alu.cpp
|
||||
src/shader_recompiler/frontend/translate/scalar_flow.cpp
|
||||
src/shader_recompiler/frontend/translate/scalar_memory.cpp
|
||||
src/shader_recompiler/frontend/translate/translate.cpp
|
||||
src/shader_recompiler/frontend/translate/translate.h
|
||||
@@ -507,6 +576,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/frontend/translate/vector_memory.cpp
|
||||
src/shader_recompiler/frontend/control_flow_graph.cpp
|
||||
src/shader_recompiler/frontend/control_flow_graph.h
|
||||
src/shader_recompiler/frontend/copy_shader.cpp
|
||||
src/shader_recompiler/frontend/copy_shader.h
|
||||
src/shader_recompiler/frontend/decode.cpp
|
||||
src/shader_recompiler/frontend/decode.h
|
||||
src/shader_recompiler/frontend/fetch_shader.cpp
|
||||
@@ -523,6 +594,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/ir/passes/ir_passes.h
|
||||
src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp
|
||||
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ring_access_elimination.cpp
|
||||
src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
|
||||
src/shader_recompiler/ir/abstract_syntax_list.h
|
||||
@@ -555,6 +627,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||
src/video_core/amdgpu/pm4_cmds.h
|
||||
src/video_core/amdgpu/pm4_opcodes.h
|
||||
src/video_core/amdgpu/resource.h
|
||||
src/video_core/amdgpu/types.h
|
||||
src/video_core/amdgpu/default_context.cpp
|
||||
src/video_core/buffer_cache/buffer.cpp
|
||||
src/video_core/buffer_cache/buffer.h
|
||||
@@ -620,8 +693,6 @@ set(IMGUI src/imgui/imgui_config.h
|
||||
src/imgui/imgui_layer.h
|
||||
src/imgui/imgui_std.h
|
||||
src/imgui/imgui_texture.h
|
||||
src/imgui/layer/video_info.cpp
|
||||
src/imgui/layer/video_info.h
|
||||
src/imgui/renderer/imgui_core.cpp
|
||||
src/imgui/renderer/imgui_core.h
|
||||
src/imgui/renderer/imgui_impl_sdl3.cpp
|
||||
@@ -650,8 +721,12 @@ qt_add_resources(RESOURCE_FILES src/shadps4.qrc)
|
||||
set(QT_GUI src/qt_gui/about_dialog.cpp
|
||||
src/qt_gui/about_dialog.h
|
||||
src/qt_gui/about_dialog.ui
|
||||
src/qt_gui/background_music_player.cpp
|
||||
src/qt_gui/background_music_player.h
|
||||
src/qt_gui/cheats_patches.cpp
|
||||
src/qt_gui/cheats_patches.h
|
||||
src/qt_gui/check_update.cpp
|
||||
src/qt_gui/check_update.h
|
||||
src/qt_gui/main_window_ui.h
|
||||
src/qt_gui/main_window.cpp
|
||||
src/qt_gui/main_window.h
|
||||
@@ -665,6 +740,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
||||
src/qt_gui/game_grid_frame.h
|
||||
src/qt_gui/game_install_dialog.cpp
|
||||
src/qt_gui/game_install_dialog.h
|
||||
src/qt_gui/install_dir_select.cpp
|
||||
src/qt_gui/install_dir_select.h
|
||||
src/qt_gui/pkg_viewer.cpp
|
||||
src/qt_gui/pkg_viewer.h
|
||||
src/qt_gui/trophy_viewer.cpp
|
||||
@@ -716,7 +793,7 @@ endif()
|
||||
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui)
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
@@ -739,9 +816,6 @@ if (APPLE)
|
||||
|
||||
# Replacement for std::chrono::time_zone
|
||||
target_link_libraries(shadps4 PRIVATE date::date-tz)
|
||||
|
||||
# Half float conversions for F16C patches
|
||||
target_link_libraries(shadps4 PRIVATE half)
|
||||
endif()
|
||||
|
||||
if (NOT ENABLE_QT_GUI)
|
||||
@@ -755,7 +829,7 @@ else()
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT_GUI)
|
||||
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network)
|
||||
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
|
||||
add_definitions(-DENABLE_QT_GUI)
|
||||
endif()
|
||||
|
||||
@@ -832,3 +906,16 @@ if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
target_link_libraries(shadps4 PRIVATE discord-rpc)
|
||||
endif()
|
||||
|
||||
# Install rules
|
||||
install(TARGETS shadps4 BUNDLE DESTINATION .)
|
||||
|
||||
if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
install(FILES ".github/shadps4.desktop" DESTINATION "share/applications")
|
||||
install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps")
|
||||
endif()
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ]
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
"intelliSenseMode": "windows-clang-x64"
|
||||
},
|
||||
{
|
||||
"name": "x64-Clang-Debug",
|
||||
@@ -20,7 +21,8 @@
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ]
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
"intelliSenseMode": "windows-clang-x64"
|
||||
},
|
||||
{
|
||||
"name": "x64-Clang-RelWithDebInfo",
|
||||
@@ -31,7 +33,8 @@
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ]
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
"intelliSenseMode": "windows-clang-x64"
|
||||
}
|
||||
]
|
||||
}
|
||||
69
README.md
@@ -26,30 +26,28 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
<p align="center">
|
||||
<a href="https://shadps4.net/">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/Sonic Mania.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/Bloodborne.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/Undertale.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/We are DOOMED.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/1.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/2.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/3.png" width="400">
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Screenshots/4.png" width="400">
|
||||
</p>
|
||||
|
||||
# General information
|
||||
|
||||
shadPS4 is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
|
||||
**shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
|
||||
|
||||
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).
|
||||
|
||||
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).
|
||||
|
||||
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).
|
||||
|
||||
To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).
|
||||
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).\
|
||||
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\
|
||||
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\
|
||||
To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\
|
||||
For those who'd like to donate to the project, we now have a [**Kofi page**](https://ko-fi.com/shadps4)!
|
||||
|
||||
# Status
|
||||
|
||||
> [!IMPORTANT]
|
||||
> shadPS4 is early in development, don't expect a flawless experience.
|
||||
|
||||
Currently, the emulator successfully runs small games like [**Sonic Mania**](https://www.youtube.com/watch?v=AAHoNzhHyCU), [**Undertale**](https://youtu.be/5zIvdy65Ro4) and it can even *somewhat* run [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE).
|
||||
Currently, the emulator can successfully run games like [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE), [**Dark Souls Remastered**](https://www.youtube.com/watch?v=-3PA-Xwszts), [**Red Dead Redemption**](https://www.youtube.com/watch?v=Al7yz_5nLag) and many other games.
|
||||
|
||||
# Why
|
||||
|
||||
@@ -69,40 +67,12 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad
|
||||
|
||||
Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-macos.md).
|
||||
|
||||
Note that macOS users need at least macOS 15 on an Apple Silicon Mac, or at least macOS 11 on an Intel Mac.
|
||||
|
||||
## Building status
|
||||
|
||||
<details>
|
||||
<summary><b>Windows</b></summary>
|
||||
|
||||
| Windows | Build status |
|
||||
|--------|--------|
|
||||
|Windows SDL Build|[](https://github.com/shadps4-emu/shadPS4/actions/workflows/windows.yml)
|
||||
|Windows Qt Build|[](https://github.com/shadps4-emu/shadPS4/actions/workflows/windows-qt.yml)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Linux</b></summary>
|
||||
|
||||
| Linux | Build status |
|
||||
|--------|--------|
|
||||
|Linux SDL Build|[](https://github.com/shadps4-emu/shadPS4/actions/workflows/linux.yml)
|
||||
|Linux Qt Build|[](https://github.com/shadps4-emu/shadPS4/actions/workflows/linux-qt.yml)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>macOS</b></summary>
|
||||
|
||||
| macOS | Build status |
|
||||
|--------|--------|
|
||||
|macOS SDL Build|[](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos.yml)
|
||||
|macOS Qt Build|[](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml)
|
||||
</details>
|
||||
> [!IMPORTANT]
|
||||
> macOS users need at least macOS 15 on Apple Silicon-based Mac devices and at least macOS 14 on Intel-based Mac devices.
|
||||
|
||||
# Debugging and reporting issues
|
||||
|
||||
For more information on how to test, debug and report issues with the emulator or games, read the [Debugging documentation](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md).
|
||||
For more information on how to test, debug and report issues with the emulator or games, read the [**Debugging documentation**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md).
|
||||
|
||||
# Keyboard mapping
|
||||
|
||||
@@ -128,7 +98,7 @@ PAD DOWN | DOWN |
|
||||
PAD LEFT | LEFT |
|
||||
PAD RIGHT | RIGHT |
|
||||
OPTIONS | RETURN |
|
||||
TOUCH PAD | SPACE |
|
||||
BACK BUTTON / TOUCH PAD | SPACE |
|
||||
L1 | Q |
|
||||
R1 | U |
|
||||
L2 | E |
|
||||
@@ -149,8 +119,7 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
|
||||
|
||||
# Contributing
|
||||
|
||||
If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.
|
||||
|
||||
If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
|
||||
Open a PR and we'll check it :)
|
||||
|
||||
# Contributors
|
||||
@@ -172,12 +141,6 @@ A few noteworthy teams/projects who've helped us along the way are:
|
||||
|
||||
- [**hydra**](https://github.com/hydra-emu/hydra): A multisystem, multiplatform emulator (chip-8, GB, NES, N64) from Paris.
|
||||
|
||||
|
||||
# Sister Projects
|
||||
|
||||
- [**Panda3DS**](https://github.com/wheremyfoodat/Panda3DS): A multiplatform 3DS emulator from our co-author wheremyfoodat.
|
||||
- [**hydra**](https://github.com/hydra-emu/hydra): A multisystem, multiplatform emulator (chip-8, GB, NES, N64) from Paris.
|
||||
|
||||
# License
|
||||
|
||||
- [**GPL-2.0 license**](https://github.com/shadps4-emu/shadPS4/blob/main/LICENSE)
|
||||
|
||||
87
REUSE.toml
Normal file
@@ -0,0 +1,87 @@
|
||||
version = 1
|
||||
|
||||
[[annotations]]
|
||||
path = [
|
||||
"REUSE.toml",
|
||||
"CMakeSettings.json",
|
||||
".github/FUNDING.yml",
|
||||
".github/shadps4.desktop",
|
||||
".github/shadps4.png",
|
||||
".gitmodules",
|
||||
"documents/changelog.txt",
|
||||
"documents/Quickstart/2.png",
|
||||
"documents/Screenshots/*",
|
||||
"scripts/ps4_names.txt",
|
||||
"src/images/about_icon.png",
|
||||
"src/images/controller_icon.png",
|
||||
"src/images/dump_icon.png",
|
||||
"src/images/exit_icon.png",
|
||||
"src/images/file_icon.png",
|
||||
"src/images/flag_china.png",
|
||||
"src/images/flag_eu.png",
|
||||
"src/images/flag_jp.png",
|
||||
"src/images/flag_unk.png",
|
||||
"src/images/flag_us.png",
|
||||
"src/images/flag_world.png",
|
||||
"src/images/folder_icon.png",
|
||||
"src/images/grid_icon.png",
|
||||
"src/images/iconsize_icon.png",
|
||||
"src/images/list_icon.png",
|
||||
"src/images/list_mode_icon.png",
|
||||
"src/images/pause_icon.png",
|
||||
"src/images/play_icon.png",
|
||||
"src/images/refresh_icon.png",
|
||||
"src/images/settings_icon.png",
|
||||
"src/images/stop_icon.png",
|
||||
"src/images/shadPS4.icns",
|
||||
"src/images/shadps4.ico",
|
||||
"src/images/themes_icon.png",
|
||||
"src/images/update_icon.png",
|
||||
"src/shadps4.qrc",
|
||||
"src/shadps4.rc",
|
||||
]
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "shadPS4 Emulator Project"
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/cmake-modules/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2009-2010 Iowa State University"
|
||||
SPDX-License-Identifier = "BSL-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/renderdoc/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/stb_image.h"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2017 Sean Barrett"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/tracy/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2017-2024 Bartosz Taudul <wolf@nereid.pl>"
|
||||
SPDX-License-Identifier = "BSD-3-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/imgui/renderer/fonts/NotoSansJP-Regular.ttf"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2012 Google Inc. All Rights Reserved."
|
||||
SPDX-License-Identifier = "OFL-1.1"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/imgui/renderer/fonts/ProggyVector-Regular.ttf"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "Copyright (c) 2004, 2005 Tristan Grimmer"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/gcn/include/**"
|
||||
SPDX-FileCopyrightText = "NONE"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
28
cmake/Findhalf.cmake
Normal file
@@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_path(half_INCLUDE_DIR NAMES half.hpp PATH_SUFFIXES half)
|
||||
|
||||
if (half_INCLUDE_DIR)
|
||||
file(STRINGS "${half_INCLUDE_DIR}/half.hpp" _ver_line
|
||||
REGEX "^// Version [0-9.]+$"
|
||||
LIMIT_COUNT 1
|
||||
)
|
||||
string(REGEX MATCH "[0-9.]+" half_VERSION "${_ver_line}")
|
||||
unset(_ver_line)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(half
|
||||
REQUIRED_VARS half_INCLUDE_DIR
|
||||
VERSION_VAR half_VERSION
|
||||
)
|
||||
|
||||
if (half_FOUND AND NOT TARGET half::half)
|
||||
add_library(half::half INTERFACE IMPORTED)
|
||||
set_target_properties(half::half PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${half_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(half_INCLUDE_DIR)
|
||||
@@ -7,21 +7,22 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
## Summary
|
||||
|
||||
- [PC Requirements](#pc-requirements)
|
||||
- [CPU](#cpu)
|
||||
- [GPU](#gpu)
|
||||
- [RAM](#ram)
|
||||
- [OS](#os)
|
||||
- [Have the latest WIP version](#have-the-latest-wip-version)
|
||||
- [Install PKG files (Games and Updates)](#install-pkg-files)
|
||||
- [Configure the emulator](#configure-the-emulator)
|
||||
- [**PC Requirements**](#minimum-pc-requirements)
|
||||
- [**CPU**](#cpu)
|
||||
- [**GPU**](#gpu)
|
||||
- [**RAM**](#ram)
|
||||
- [**OS**](#os)
|
||||
- [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4)
|
||||
- [**Install PKG files (Games and Updates)**](#install-pkg-files)
|
||||
- [**Configure the emulator**](#configure-the-emulator)
|
||||
|
||||
## PC Requirements
|
||||
## Minimum PC requirements
|
||||
|
||||
### CPU
|
||||
|
||||
- A processor with at least 4 cores and 6 threads
|
||||
- Above 2.5 GHz frequency
|
||||
- required support AVX2 extension or Rosetta 2 on ARM
|
||||
|
||||
### GPU
|
||||
|
||||
@@ -37,45 +38,25 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
- Windows 10 or Ubuntu 22.04
|
||||
|
||||
## How to run the latest Work-in-Progress builds of ShadPS4
|
||||
## How to run the latest Work-in-Progress builds of shadPS4
|
||||
|
||||
1. Go to <https://github.com/shadps4-emu/shadPS4/actions> and make sure you are logged into your GitHub account (important!)
|
||||
2. On the left side of the page, select your operating system of choice (the "**qt**" versions have a user interface, which is probably the one you want. The others are SDL versions, which can only be run via command line). 
|
||||
1. Go to <https://github.com/shadps4-emu/shadPS4/releases> In the release identified as 'pre-release' click on the down arrow(Assets), select your operating system of choice (the "**qt**" versions have a user interface, which is probably the one you want. The others are SDL versions, which can only be run via command line).
|
||||

|
||||
|
||||
3. In the workflow list, select the latest entry with a green :white_check_mark: icon in front of it. (or the latest entry for whatever pull request you wish to test). 
|
||||
2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder.
|
||||
|
||||
4. On the bottom of this page, select the name of the file, and it should start downloading. (If there is no file here, double check that you are indeed logged into a GitHub account, and that there is a green :white_check_mark: icon. 
|
||||
|
||||
5. Once downloaded, extract to its own folder, and run ShadPS4's executable from the extracted folder.
|
||||
|
||||
6. Upon first launch, ShadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that ShadPS4 can use to install your PKG files to.
|
||||
3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that shadPS4 can use to install your PKG files to.
|
||||
|
||||
## Install PKG files
|
||||
|
||||
To install PKG files (game and updates), you will need the Qt application (with UI). You will have to go to "File" then to "Install Packages (PKG)", a window will open then you will have to select the files. You can install multiple PKG files at once. Once finished, the game should appear in the application.
|
||||
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/2.png" width="800"></a>
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/2.png" width="800">
|
||||
|
||||
## Configure the emulator
|
||||
|
||||
You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.\
|
||||
Some settings may be related to more technical development and debugging. For more information on those, see [Debugging](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
|
||||
To configure the emulator, you can go through the interface and go to "settings".
|
||||
|
||||
Here's a list of configuration entries that are worth changing:
|
||||
|
||||
- `[General]`
|
||||
|
||||
- `Fullscreen`: Display the game in a full screen borderless window.
|
||||
|
||||
- `logType`: Configures logging synchronization (`sync`/`async`)
|
||||
- It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance.
|
||||
- Use when sending logs to developers. See more about [reporting issues](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#reporting-and-communicating-about-issues).
|
||||
- `logFilter`: Sets the logging category for various logging classes.
|
||||
- Format: `<class>:<level> ...`, `<class.*>:<level> <*:level> ...`
|
||||
- Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it.
|
||||
- Examples:
|
||||
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages.
|
||||
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Error Render.Vulkan:Info`
|
||||
|
||||
- `[GPU]`
|
||||
- `screenWidth` and `screenHeight`: Configures the game window width and height.
|
||||
You can also configure the emulator by editing the `config.toml` file located in the `user` folder created after the application is started (Mostly useful if you are using the SDL version).
|
||||
Some settings may be related to more technical development and debugging.\
|
||||
For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
|
||||
BIN
documents/Screenshots/1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
documents/Screenshots/2.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
documents/Screenshots/3.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
documents/Screenshots/4.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 350 KiB |
|
Before Width: | Height: | Size: 850 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 175 KiB |
@@ -9,22 +9,22 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#### Debian & Ubuntu
|
||||
```
|
||||
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev
|
||||
sudo apt install build-essential clang git cmake libasound2-dev libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev vulkan-validationlayers
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
```
|
||||
sudo dnf install alsa-lib-devel cmake libatomic libevdev-devel libudev-devel openal-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel
|
||||
sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel libXext-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers
|
||||
```
|
||||
|
||||
#### Arch Linux
|
||||
```
|
||||
sudo pacman -S openal cmake vulkan-validation-layers qt6-base qt6-declarative qt6-multimedia sdl2 sndio jack2 base-devel
|
||||
sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers
|
||||
```
|
||||
|
||||
#### OpenSUSE
|
||||
```
|
||||
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt6-qtbase-devel libqt6-qtmultimedia-devel libqt6-qtsvg-devel libQt6Gui-private-headers-devel libevdev-devel libsndio7_1 libjack-devel
|
||||
sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers
|
||||
```
|
||||
### Cloning and compiling:
|
||||
|
||||
@@ -34,9 +34,11 @@ git clone --recursive https://github.com/shadps4-emu/shadPS4.git
|
||||
cd shadPS4
|
||||
```
|
||||
|
||||
Generate the build directory in the shadPS4 directory. To enable the QT GUI, pass the ```-DENABLE_QT_GUI=ON``` flag:
|
||||
Generate the build directory in the shadPS4 directory. To disable the QT GUI, remove the ```-DENABLE_QT_GUI=ON``` flag:
|
||||
|
||||
**Note**: Clang is the compiler used for official builds and CI. If you build with GCC, you might encounter issues—please report any you find. If you choose to use GCC, we recommend building with Clang at least once before submitting a pull request.
|
||||
```
|
||||
cmake -S . -B build/ -DENABLE_QT_GUI=ON
|
||||
cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
```
|
||||
|
||||
Enter the directory:
|
||||
|
||||
@@ -80,7 +80,7 @@ Normal x86-based computers, follow:
|
||||
1. Open "MSYS2 MINGW64" from your new applications
|
||||
2. Run `pacman -Syu`, let it complete;
|
||||
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia`
|
||||
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
|
||||
5. Run `cd shadPS4`
|
||||
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
|
||||
@@ -94,7 +94,7 @@ ARM64-based computers, follow:
|
||||
1. Open "MSYS2 CLANGARM64" from your new applications
|
||||
2. Run `pacman -Syu`, let it complete;
|
||||
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia`
|
||||
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
|
||||
5. Run `cd shadPS4`
|
||||
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
v0.3.0 23/09/2024 - codename broamic
|
||||
=================
|
||||
|
||||
- Cheat/Patching support
|
||||
- DLC support
|
||||
- New translations support (26 languages)
|
||||
- Support for unlocking trophies
|
||||
- Support for more controllers (Dualshock and Xbox)
|
||||
- Many GUI improvements
|
||||
- AVplayer
|
||||
|
||||
v0.2.0 15/08/2024 - codename validptr
|
||||
=================
|
||||
- Adding macOS support
|
||||
|
||||
29
externals/CMakeLists.txt
vendored
@@ -3,7 +3,10 @@
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(BUILD_TESTING OFF)
|
||||
set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
|
||||
set_directory_properties(PROPERTIES
|
||||
EXCLUDE_FROM_ALL ON
|
||||
SYSTEM ON
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
# Silence "deprecation" warnings
|
||||
@@ -13,11 +16,8 @@ endif()
|
||||
# Boost
|
||||
if (NOT TARGET Boost::headers)
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "")
|
||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
add_library(Boost::headers ALIAS boost)
|
||||
add_subdirectory(ext-boost)
|
||||
endif()
|
||||
|
||||
# fmtlib
|
||||
@@ -77,7 +77,7 @@ endif()
|
||||
# RenderDoc
|
||||
if (NOT TARGET RenderDoc::API)
|
||||
add_library(renderdoc INTERFACE)
|
||||
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
||||
target_include_directories(renderdoc INTERFACE ./renderdoc)
|
||||
add_library(RenderDoc::API ALIAS renderdoc)
|
||||
endif()
|
||||
|
||||
@@ -141,11 +141,14 @@ if (WIN32)
|
||||
target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# half
|
||||
# half
|
||||
if (NOT TARGET half::half)
|
||||
add_library(half INTERFACE)
|
||||
target_include_directories(half INTERFACE half/include)
|
||||
add_library(half::half ALIAS half)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# date
|
||||
if (NOT TARGET date::date-tz)
|
||||
option(BUILD_TZ_LIB "" ON)
|
||||
@@ -183,3 +186,13 @@ add_subdirectory(tracy)
|
||||
if (NOT TARGET pugixml::pugixml)
|
||||
add_subdirectory(pugixml)
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
add_subdirectory(discord-rpc/)
|
||||
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
|
||||
endif()
|
||||
|
||||
# GCN Headers
|
||||
add_subdirectory(gcn)
|
||||
|
||||
2
externals/date
vendored
1
externals/discord-rpc
vendored
Submodule
2
externals/ext-boost
vendored
2
externals/fmt
vendored
8
externals/gcn/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
project(gcn LANGUAGES CXX)
|
||||
|
||||
add_library(gcn dummy.cpp)
|
||||
|
||||
target_include_directories(gcn INTERFACE include)
|
||||
2
externals/gcn/dummy.cpp
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
15339
externals/gcn/include/gcn/si_ci_vi_merged_offset.h
vendored
Normal file
117
externals/gcn/include/gcn/si_ci_vi_merged_pm4_it_opcodes.h
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2021 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
|
||||
#ifndef PM4_IT_OPCODES_H
|
||||
#define PM4_IT_OPCODES_H
|
||||
|
||||
enum IT_OpCodeType {
|
||||
IT_NOP = 0x10,
|
||||
IT_SET_BASE = 0x11,
|
||||
IT_CLEAR_STATE = 0x12,
|
||||
IT_INDEX_BUFFER_SIZE = 0x13,
|
||||
IT_DISPATCH_DIRECT = 0x15,
|
||||
IT_DISPATCH_INDIRECT = 0x16,
|
||||
IT_ATOMIC_GDS = 0x1D,
|
||||
IT_ATOMIC = 0x1E,
|
||||
IT_OCCLUSION_QUERY = 0x1F,
|
||||
IT_SET_PREDICATION = 0x20,
|
||||
IT_REG_RMW = 0x21,
|
||||
IT_COND_EXEC = 0x22,
|
||||
IT_PRED_EXEC = 0x23,
|
||||
IT_DRAW_INDIRECT = 0x24,
|
||||
IT_DRAW_INDEX_INDIRECT = 0x25,
|
||||
IT_INDEX_BASE = 0x26,
|
||||
IT_DRAW_INDEX_2 = 0x27,
|
||||
IT_CONTEXT_CONTROL = 0x28,
|
||||
IT_INDEX_TYPE = 0x2A,
|
||||
IT_DRAW_INDIRECT_MULTI = 0x2C,
|
||||
IT_DRAW_INDEX_AUTO = 0x2D,
|
||||
IT_NUM_INSTANCES = 0x2F,
|
||||
IT_DRAW_INDEX_MULTI_AUTO = 0x30,
|
||||
IT_INDIRECT_BUFFER_CNST = 0x33,
|
||||
IT_STRMOUT_BUFFER_UPDATE = 0x34,
|
||||
IT_DRAW_INDEX_OFFSET_2 = 0x35,
|
||||
IT_WRITE_DATA = 0x37,
|
||||
IT_DRAW_INDEX_INDIRECT_MULTI = 0x38,
|
||||
IT_MEM_SEMAPHORE = 0x39,
|
||||
IT_COPY_DW__SI__CI = 0x3B,
|
||||
IT_WAIT_REG_MEM = 0x3C,
|
||||
IT_INDIRECT_BUFFER = 0x3F,
|
||||
IT_COND_INDIRECT_BUFFER = 0x3F,
|
||||
IT_COPY_DATA = 0x40,
|
||||
IT_CP_DMA = 0x41,
|
||||
IT_PFP_SYNC_ME = 0x42,
|
||||
IT_SURFACE_SYNC = 0x43,
|
||||
IT_COND_WRITE = 0x45,
|
||||
IT_EVENT_WRITE = 0x46,
|
||||
IT_EVENT_WRITE_EOP = 0x47,
|
||||
IT_EVENT_WRITE_EOS = 0x48,
|
||||
IT_PREAMBLE_CNTL = 0x4A,
|
||||
IT_CONTEXT_REG_RMW = 0x51,
|
||||
IT_LOAD_SH_REG = 0x5F,
|
||||
IT_LOAD_CONFIG_REG = 0x60,
|
||||
IT_LOAD_CONTEXT_REG = 0x61,
|
||||
IT_SET_CONFIG_REG = 0x68,
|
||||
IT_SET_CONTEXT_REG = 0x69,
|
||||
IT_SET_CONTEXT_REG_INDIRECT = 0x73,
|
||||
IT_SET_SH_REG = 0x76,
|
||||
IT_SET_SH_REG_OFFSET = 0x77,
|
||||
IT_SCRATCH_RAM_WRITE = 0x7D,
|
||||
IT_SCRATCH_RAM_READ = 0x7E,
|
||||
IT_LOAD_CONST_RAM = 0x80,
|
||||
IT_WRITE_CONST_RAM = 0x81,
|
||||
IT_DUMP_CONST_RAM = 0x83,
|
||||
IT_INCREMENT_CE_COUNTER = 0x84,
|
||||
IT_INCREMENT_DE_COUNTER = 0x85,
|
||||
IT_WAIT_ON_CE_COUNTER = 0x86,
|
||||
IT_WAIT_ON_DE_COUNTER__SI = 0x87,
|
||||
IT_WAIT_ON_DE_COUNTER_DIFF = 0x88,
|
||||
IT_SWITCH_BUFFER = 0x8B,
|
||||
IT_DRAW_PREAMBLE__CI__VI = 0x36,
|
||||
IT_RELEASE_MEM__CI__VI = 0x49,
|
||||
IT_DMA_DATA__CI__VI = 0x50,
|
||||
IT_ACQUIRE_MEM__CI__VI = 0x58,
|
||||
IT_REWIND__CI__VI = 0x59,
|
||||
IT_LOAD_UCONFIG_REG__CI__VI = 0x5E,
|
||||
IT_SET_QUEUE_REG__CI__VI = 0x78,
|
||||
IT_SET_UCONFIG_REG__CI__VI = 0x79,
|
||||
IT_INDEX_ATTRIBUTES_INDIRECT__CI__VI = 0x91,
|
||||
IT_SET_SH_REG_INDEX__CI__VI = 0x9B,
|
||||
IT_SET_RESOURCES__CI__VI = 0xA0,
|
||||
IT_MAP_PROCESS__CI__VI = 0xA1,
|
||||
IT_MAP_QUEUES__CI__VI = 0xA2,
|
||||
IT_UNMAP_QUEUES__CI__VI = 0xA3,
|
||||
IT_QUERY_STATUS__CI__VI = 0xA4,
|
||||
IT_RUN_LIST__CI__VI = 0xA5,
|
||||
IT_LOAD_SH_REG_INDEX__VI = 0x63,
|
||||
IT_LOAD_CONTEXT_REG_INDEX__VI = 0x9F,
|
||||
IT_DUMP_CONST_RAM_OFFSET__VI = 0x9E,
|
||||
};
|
||||
|
||||
#define PM4_TYPE_0 0
|
||||
#define PM4_TYPE_2 2
|
||||
#define PM4_TYPE_3 3
|
||||
|
||||
#endif
|
||||
2
externals/glslang
vendored
2
externals/magic_enum
vendored
2
externals/pugixml
vendored
2
externals/robin-map
vendored
2
externals/sdl3
vendored
2
externals/sirit
vendored
2
externals/toml11
vendored
2
externals/vma
vendored
2
externals/vulkan-headers
vendored
2
externals/xbyak
vendored
2
externals/xxhash
vendored
2
externals/zydis
vendored
@@ -14,7 +14,9 @@
|
||||
|
||||
namespace Audio {
|
||||
|
||||
int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
|
||||
|
||||
s32 SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||
Libraries::AudioOut::OrbisAudioOutParamFormat format) {
|
||||
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
||||
std::unique_lock lock{m_mutex};
|
||||
@@ -80,7 +82,7 @@ int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||
SDL_zero(fmt);
|
||||
fmt.format = sampleFormat;
|
||||
fmt.channels = port.channels_num;
|
||||
fmt.freq = 48000;
|
||||
fmt.freq = freq; // Set frequency from the argument
|
||||
port.stream =
|
||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL);
|
||||
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port.stream));
|
||||
@@ -88,7 +90,8 @@ int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // all ports are used
|
||||
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
|
||||
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; // all ports are used
|
||||
}
|
||||
|
||||
s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||
@@ -97,27 +100,28 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||
if (!port.isOpen) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||
}
|
||||
if (ptr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
// TODO mixing channels
|
||||
int result = SDL_PutAudioStreamData(port.stream, ptr,
|
||||
port.samples_num * port.sample_size * port.channels_num);
|
||||
// TODO find a correct value 8192 is estimated
|
||||
while (SDL_GetAudioStreamAvailable(port.stream) > 65536) {
|
||||
|
||||
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
|
||||
|
||||
bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
|
||||
|
||||
lock.unlock(); // Unlock only after necessary operations
|
||||
|
||||
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
|
||||
SDL_Delay(0);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result ? ORBIS_OK : -1;
|
||||
}
|
||||
|
||||
bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
|
||||
s32 SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
|
||||
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
||||
std::shared_lock lock{m_mutex};
|
||||
auto& port = portsOut[handle - 1];
|
||||
if (!port.isOpen) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) {
|
||||
auto bit = bitflag & 0x1u;
|
||||
|
||||
@@ -147,16 +151,16 @@ bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
bool SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) {
|
||||
s32 SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) {
|
||||
std::shared_lock lock{m_mutex};
|
||||
auto& port = portsOut[handle - 1];
|
||||
*type = port.type;
|
||||
*channels_num = port.channels_num;
|
||||
|
||||
return true;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
} // namespace Audio
|
||||
|
||||
@@ -14,11 +14,11 @@ public:
|
||||
SDLAudio() = default;
|
||||
virtual ~SDLAudio() = default;
|
||||
|
||||
int AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||
s32 AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||
Libraries::AudioOut::OrbisAudioOutParamFormat format);
|
||||
s32 AudioOutOutput(s32 handle, const void* ptr);
|
||||
bool AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume);
|
||||
bool AudioOutGetStatus(s32 handle, int* type, int* channels_num);
|
||||
s32 AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume);
|
||||
s32 AudioOutGetStatus(s32 handle, int* type, int* channels_num);
|
||||
|
||||
private:
|
||||
struct PortOut {
|
||||
@@ -33,8 +33,7 @@ private:
|
||||
bool isOpen = false;
|
||||
};
|
||||
std::shared_mutex m_mutex;
|
||||
std::array<PortOut, 22> portsOut; // main up to 8 ports , BGM 1 port , voice up to 4 ports ,
|
||||
// personal up to 4 ports , padspk up to 5 ports , aux 1 port
|
||||
std::array<PortOut, Libraries::AudioOut::SCE_AUDIO_OUT_NUM_PORTS> portsOut;
|
||||
};
|
||||
|
||||
} // namespace Audio
|
||||
|
||||
@@ -28,4 +28,16 @@ template <typename T>
|
||||
return (value & 0x3FFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool Is64KBAligned(T value) {
|
||||
return (value & 0xFFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool Is2MBAligned(T value) {
|
||||
return (value & 0x1FFFFF) == 0;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -81,7 +81,9 @@
|
||||
#pragma pack(1)
|
||||
template <std::size_t Position, std::size_t Bits, typename T>
|
||||
struct BitField {
|
||||
private:
|
||||
|
||||
using Type = T;
|
||||
|
||||
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
@@ -92,7 +94,6 @@ private:
|
||||
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
|
||||
@@ -3,28 +3,54 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <common/version.h>
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/xchar.h> // for wstring support
|
||||
#include <toml.hpp>
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/path_util.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace toml {
|
||||
template <typename TC, typename K>
|
||||
std::filesystem::path find_fs_path_or(const basic_value<TC>& v, const K& ky,
|
||||
std::filesystem::path opt) {
|
||||
try {
|
||||
auto str = find<std::string>(v, ky);
|
||||
if (str.empty()) {
|
||||
return opt;
|
||||
}
|
||||
std::u8string u8str{(char8_t*)&str.front(), (char8_t*)&str.back() + 1};
|
||||
return std::filesystem::path{u8str};
|
||||
} catch (...) {
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
} // namespace toml
|
||||
|
||||
namespace Config {
|
||||
|
||||
static bool isNeo = false;
|
||||
static bool isFullscreen = false;
|
||||
static bool playBGM = false;
|
||||
static int BGMvolume = 50;
|
||||
static bool enableDiscordRPC = false;
|
||||
static u32 screenWidth = 1280;
|
||||
static u32 screenHeight = 720;
|
||||
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
|
||||
static std::string logFilter;
|
||||
static std::string logType = "async";
|
||||
static std::string userName = "shadPS4";
|
||||
static std::string updateChannel;
|
||||
static std::string backButtonBehavior = "left";
|
||||
static bool useSpecialPad = false;
|
||||
static int specialPadClass = 1;
|
||||
static bool isDebugDump = false;
|
||||
static bool isShowSplash = false;
|
||||
static bool isAutoUpdate = false;
|
||||
static bool isNullGpu = false;
|
||||
static bool shouldCopyGPUBuffers = false;
|
||||
static bool shouldDumpShaders = false;
|
||||
static bool shouldDumpPM4 = false;
|
||||
static u32 vblankDivider = 1;
|
||||
static bool vkValidation = false;
|
||||
static bool vkValidationSync = false;
|
||||
@@ -32,9 +58,13 @@ static bool vkValidationGpu = false;
|
||||
static bool rdocEnable = false;
|
||||
static bool vkMarkers = false;
|
||||
static bool vkCrashDiagnostic = false;
|
||||
static s16 cursorState = HideCursorState::Idle;
|
||||
static int cursorHideTimeout = 5; // 5 seconds (default)
|
||||
static bool separateupdatefolder = false;
|
||||
|
||||
// Gui
|
||||
std::string settings_install_dir = "";
|
||||
std::vector<std::filesystem::path> settings_install_dirs = {};
|
||||
std::filesystem::path settings_addon_install_dir = {};
|
||||
u32 main_window_geometry_x = 400;
|
||||
u32 main_window_geometry_y = 400;
|
||||
u32 main_window_geometry_w = 1280;
|
||||
@@ -62,6 +92,26 @@ bool isFullscreenMode() {
|
||||
return isFullscreen;
|
||||
}
|
||||
|
||||
bool getPlayBGM() {
|
||||
return playBGM;
|
||||
}
|
||||
|
||||
int getBGMvolume() {
|
||||
return BGMvolume;
|
||||
}
|
||||
|
||||
bool getEnableDiscordRPC() {
|
||||
return enableDiscordRPC;
|
||||
}
|
||||
|
||||
s16 getCursorState() {
|
||||
return cursorState;
|
||||
}
|
||||
|
||||
int getCursorHideTimeout() {
|
||||
return cursorHideTimeout;
|
||||
}
|
||||
|
||||
u32 getScreenWidth() {
|
||||
return screenWidth;
|
||||
}
|
||||
@@ -86,6 +136,14 @@ std::string getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
std::string getUpdateChannel() {
|
||||
return updateChannel;
|
||||
}
|
||||
|
||||
std::string getBackButtonBehavior() {
|
||||
return backButtonBehavior;
|
||||
}
|
||||
|
||||
bool getUseSpecialPad() {
|
||||
return useSpecialPad;
|
||||
}
|
||||
@@ -102,6 +160,10 @@ bool showSplash() {
|
||||
return isShowSplash;
|
||||
}
|
||||
|
||||
bool autoUpdate() {
|
||||
return isAutoUpdate;
|
||||
}
|
||||
|
||||
bool nullGpu() {
|
||||
return isNullGpu;
|
||||
}
|
||||
@@ -114,10 +176,6 @@ bool dumpShaders() {
|
||||
return shouldDumpShaders;
|
||||
}
|
||||
|
||||
bool dumpPM4() {
|
||||
return shouldDumpPM4;
|
||||
}
|
||||
|
||||
bool isRdocEnabled() {
|
||||
return rdocEnable;
|
||||
}
|
||||
@@ -150,6 +208,10 @@ bool vkCrashDiagnosticEnabled() {
|
||||
return vkCrashDiagnostic;
|
||||
}
|
||||
|
||||
bool getSeparateUpdateEnabled() {
|
||||
return separateupdatefolder;
|
||||
}
|
||||
|
||||
void setGpuId(s32 selectedGpuId) {
|
||||
gpuId = selectedGpuId;
|
||||
}
|
||||
@@ -170,6 +232,10 @@ void setShowSplash(bool enable) {
|
||||
isShowSplash = enable;
|
||||
}
|
||||
|
||||
void setAutoUpdate(bool enable) {
|
||||
isAutoUpdate = enable;
|
||||
}
|
||||
|
||||
void setNullGpu(bool enable) {
|
||||
isNullGpu = enable;
|
||||
}
|
||||
@@ -182,10 +248,6 @@ void setDumpShaders(bool enable) {
|
||||
shouldDumpShaders = enable;
|
||||
}
|
||||
|
||||
void setDumpPM4(bool enable) {
|
||||
shouldDumpPM4 = enable;
|
||||
}
|
||||
|
||||
void setVkValidation(bool enable) {
|
||||
vkValidation = enable;
|
||||
}
|
||||
@@ -206,6 +268,26 @@ void setFullscreenMode(bool enable) {
|
||||
isFullscreen = enable;
|
||||
}
|
||||
|
||||
void setPlayBGM(bool enable) {
|
||||
playBGM = enable;
|
||||
}
|
||||
|
||||
void setBGMvolume(int volume) {
|
||||
BGMvolume = volume;
|
||||
}
|
||||
|
||||
void setEnableDiscordRPC(bool enable) {
|
||||
enableDiscordRPC = enable;
|
||||
}
|
||||
|
||||
void setCursorState(s16 newCursorState) {
|
||||
cursorState = newCursorState;
|
||||
}
|
||||
|
||||
void setCursorHideTimeout(int newcursorHideTimeout) {
|
||||
cursorHideTimeout = newcursorHideTimeout;
|
||||
}
|
||||
|
||||
void setLanguage(u32 language) {
|
||||
m_language = language;
|
||||
}
|
||||
@@ -226,6 +308,14 @@ void setUserName(const std::string& type) {
|
||||
userName = type;
|
||||
}
|
||||
|
||||
void setUpdateChannel(const std::string& type) {
|
||||
updateChannel = type;
|
||||
}
|
||||
|
||||
void setBackButtonBehavior(const std::string& type) {
|
||||
backButtonBehavior = type;
|
||||
}
|
||||
|
||||
void setUseSpecialPad(bool use) {
|
||||
useSpecialPad = use;
|
||||
}
|
||||
@@ -234,14 +324,32 @@ void setSpecialPadClass(int type) {
|
||||
specialPadClass = type;
|
||||
}
|
||||
|
||||
void setSeparateUpdateEnabled(bool use) {
|
||||
separateupdatefolder = use;
|
||||
}
|
||||
|
||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
|
||||
main_window_geometry_x = x;
|
||||
main_window_geometry_y = y;
|
||||
main_window_geometry_w = w;
|
||||
main_window_geometry_h = h;
|
||||
}
|
||||
void setGameInstallDir(const std::string& dir) {
|
||||
settings_install_dir = dir;
|
||||
bool addGameInstallDir(const std::filesystem::path& dir) {
|
||||
if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) ==
|
||||
settings_install_dirs.end()) {
|
||||
settings_install_dirs.push_back(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void removeGameInstallDir(const std::filesystem::path& dir) {
|
||||
auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir);
|
||||
if (iterator != settings_install_dirs.end()) {
|
||||
settings_install_dirs.erase(iterator);
|
||||
}
|
||||
}
|
||||
void setAddonInstallDir(const std::filesystem::path& dir) {
|
||||
settings_addon_install_dir = dir;
|
||||
}
|
||||
void setMainWindowTheme(u32 theme) {
|
||||
mw_themes = theme;
|
||||
@@ -296,8 +404,15 @@ u32 getMainWindowGeometryW() {
|
||||
u32 getMainWindowGeometryH() {
|
||||
return main_window_geometry_h;
|
||||
}
|
||||
std::string getGameInstallDir() {
|
||||
return settings_install_dir;
|
||||
const std::vector<std::filesystem::path>& getGameInstallDirs() {
|
||||
return settings_install_dirs;
|
||||
}
|
||||
std::filesystem::path getAddonInstallDir() {
|
||||
if (settings_addon_install_dir.empty()) {
|
||||
// Default for users without a config file or a config file from before this option existed
|
||||
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "addcont";
|
||||
}
|
||||
return settings_addon_install_dir;
|
||||
}
|
||||
u32 getMainWindowTheme() {
|
||||
return mw_themes;
|
||||
@@ -351,7 +466,10 @@ void load(const std::filesystem::path& path) {
|
||||
toml::value data;
|
||||
|
||||
try {
|
||||
data = toml::parse(path);
|
||||
std::ifstream ifs;
|
||||
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
ifs.open(path, std::ios_base::binary);
|
||||
data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
} catch (std::exception& ex) {
|
||||
fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what());
|
||||
return;
|
||||
@@ -361,15 +479,28 @@ void load(const std::filesystem::path& path) {
|
||||
|
||||
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
|
||||
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
|
||||
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
||||
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
||||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||
logType = toml::find_or<std::string>(general, "logType", "sync");
|
||||
userName = toml::find_or<std::string>(general, "userName", "shadPS4");
|
||||
if (Common::isRelease) {
|
||||
updateChannel = toml::find_or<std::string>(general, "updateChannel", "Release");
|
||||
} else {
|
||||
updateChannel = toml::find_or<std::string>(general, "updateChannel", "Nightly");
|
||||
}
|
||||
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
||||
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
||||
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
||||
}
|
||||
|
||||
if (data.contains("Input")) {
|
||||
const toml::value& input = data.at("Input");
|
||||
|
||||
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
|
||||
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
|
||||
backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left");
|
||||
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
|
||||
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
|
||||
}
|
||||
@@ -382,7 +513,6 @@ void load(const std::filesystem::path& path) {
|
||||
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
|
||||
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false);
|
||||
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
|
||||
shouldDumpPM4 = toml::find_or<bool>(gpu, "dumpPM4", false);
|
||||
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
|
||||
}
|
||||
|
||||
@@ -414,7 +544,20 @@ void load(const std::filesystem::path& path) {
|
||||
mw_themes = toml::find_or<int>(gui, "theme", 0);
|
||||
m_window_size_W = toml::find_or<int>(gui, "mw_width", 0);
|
||||
m_window_size_H = toml::find_or<int>(gui, "mw_height", 0);
|
||||
settings_install_dir = toml::find_or<std::string>(gui, "installDir", "");
|
||||
|
||||
// TODO Migration code, after a major release this should be removed.
|
||||
auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {});
|
||||
if (!old_game_install_dir.empty()) {
|
||||
addGameInstallDir(std::filesystem::path{old_game_install_dir});
|
||||
} else {
|
||||
const auto install_dir_array =
|
||||
toml::find_or<std::vector<std::string>>(gui, "installDirs", {});
|
||||
for (const auto& dir : install_dir_array) {
|
||||
addGameInstallDir(std::filesystem::path{dir});
|
||||
}
|
||||
}
|
||||
|
||||
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
|
||||
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
|
||||
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
|
||||
main_window_geometry_w = toml::find_or<int>(gui, "geometry_w", 0);
|
||||
@@ -438,25 +581,36 @@ void save(const std::filesystem::path& path) {
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(path, error)) {
|
||||
try {
|
||||
data = toml::parse(path);
|
||||
std::ifstream ifs;
|
||||
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
ifs.open(path, std::ios_base::binary);
|
||||
data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
} catch (const std::exception& ex) {
|
||||
fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (error) {
|
||||
fmt::print("Filesystem error accessing {} (error: {})\n", path.string(),
|
||||
error.message().c_str());
|
||||
fmt::print("Filesystem error: {}\n", error.message());
|
||||
}
|
||||
fmt::print("Saving new configuration file {}\n", path.string());
|
||||
fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
|
||||
}
|
||||
|
||||
data["General"]["isPS4Pro"] = isNeo;
|
||||
data["General"]["Fullscreen"] = isFullscreen;
|
||||
data["General"]["playBGM"] = playBGM;
|
||||
data["General"]["BGMvolume"] = BGMvolume;
|
||||
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
||||
data["General"]["logFilter"] = logFilter;
|
||||
data["General"]["logType"] = logType;
|
||||
data["General"]["userName"] = userName;
|
||||
data["General"]["updateChannel"] = updateChannel;
|
||||
data["General"]["showSplash"] = isShowSplash;
|
||||
data["General"]["autoUpdate"] = isAutoUpdate;
|
||||
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
||||
data["Input"]["cursorState"] = cursorState;
|
||||
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
|
||||
data["Input"]["backButtonBehavior"] = backButtonBehavior;
|
||||
data["Input"]["useSpecialPad"] = useSpecialPad;
|
||||
data["Input"]["specialPadClass"] = specialPadClass;
|
||||
data["GPU"]["screenWidth"] = screenWidth;
|
||||
@@ -464,7 +618,6 @@ void save(const std::filesystem::path& path) {
|
||||
data["GPU"]["nullGpu"] = isNullGpu;
|
||||
data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers;
|
||||
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
||||
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
||||
data["GPU"]["vblankDivider"] = vblankDivider;
|
||||
data["Vulkan"]["gpuId"] = gpuId;
|
||||
data["Vulkan"]["validation"] = vkValidation;
|
||||
@@ -482,7 +635,15 @@ void save(const std::filesystem::path& path) {
|
||||
data["GUI"]["gameTableMode"] = m_table_mode;
|
||||
data["GUI"]["mw_width"] = m_window_size_W;
|
||||
data["GUI"]["mw_height"] = m_window_size_H;
|
||||
data["GUI"]["installDir"] = settings_install_dir;
|
||||
|
||||
std::vector<std::string> install_dirs;
|
||||
for (const auto& dirString : settings_install_dirs) {
|
||||
install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});
|
||||
}
|
||||
data["GUI"]["installDirs"] = install_dirs;
|
||||
|
||||
data["GUI"]["addonInstallDir"] =
|
||||
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
||||
data["GUI"]["geometry_x"] = main_window_geometry_x;
|
||||
data["GUI"]["geometry_y"] = main_window_geometry_y;
|
||||
data["GUI"]["geometry_w"] = main_window_geometry_w;
|
||||
@@ -494,6 +655,9 @@ void save(const std::filesystem::path& path) {
|
||||
|
||||
data["Settings"]["consoleLanguage"] = m_language;
|
||||
|
||||
// TODO Migration code, after a major release this should be removed.
|
||||
data.at("GUI").as_table().erase("installDir");
|
||||
|
||||
std::ofstream file(path, std::ios::out);
|
||||
file << data;
|
||||
file.close();
|
||||
@@ -502,18 +666,29 @@ void save(const std::filesystem::path& path) {
|
||||
void setDefaultValues() {
|
||||
isNeo = false;
|
||||
isFullscreen = false;
|
||||
playBGM = false;
|
||||
BGMvolume = 50;
|
||||
enableDiscordRPC = true;
|
||||
screenWidth = 1280;
|
||||
screenHeight = 720;
|
||||
logFilter = "";
|
||||
logType = "async";
|
||||
userName = "shadPS4";
|
||||
if (Common::isRelease) {
|
||||
updateChannel = "Release";
|
||||
} else {
|
||||
updateChannel = "Nightly";
|
||||
}
|
||||
cursorState = HideCursorState::Idle;
|
||||
cursorHideTimeout = 5;
|
||||
backButtonBehavior = "left";
|
||||
useSpecialPad = false;
|
||||
specialPadClass = 1;
|
||||
isDebugDump = false;
|
||||
isShowSplash = false;
|
||||
isAutoUpdate = false;
|
||||
isNullGpu = false;
|
||||
shouldDumpShaders = false;
|
||||
shouldDumpPM4 = false;
|
||||
vblankDivider = 1;
|
||||
vkValidation = false;
|
||||
vkValidationSync = false;
|
||||
|
||||
@@ -8,15 +8,27 @@
|
||||
#include "types.h"
|
||||
|
||||
namespace Config {
|
||||
|
||||
enum HideCursorState : s16 { Never, Idle, Always };
|
||||
|
||||
void load(const std::filesystem::path& path);
|
||||
void save(const std::filesystem::path& path);
|
||||
|
||||
bool isNeoMode();
|
||||
bool isFullscreenMode();
|
||||
bool getPlayBGM();
|
||||
int getBGMvolume();
|
||||
bool getEnableDiscordRPC();
|
||||
bool getSeparateUpdateEnabled();
|
||||
|
||||
std::string getLogFilter();
|
||||
std::string getLogType();
|
||||
std::string getUserName();
|
||||
std::string getUpdateChannel();
|
||||
|
||||
s16 getCursorState();
|
||||
int getCursorHideTimeout();
|
||||
std::string getBackButtonBehavior();
|
||||
bool getUseSpecialPad();
|
||||
int getSpecialPadClass();
|
||||
|
||||
@@ -26,28 +38,36 @@ s32 getGpuId();
|
||||
|
||||
bool debugDump();
|
||||
bool showSplash();
|
||||
bool autoUpdate();
|
||||
bool nullGpu();
|
||||
bool copyGPUCmdBuffers();
|
||||
bool dumpShaders();
|
||||
bool dumpPM4();
|
||||
bool isRdocEnabled();
|
||||
u32 vblankDiv();
|
||||
|
||||
void setDebugDump(bool enable);
|
||||
void setShowSplash(bool enable);
|
||||
void setAutoUpdate(bool enable);
|
||||
void setNullGpu(bool enable);
|
||||
void setCopyGPUCmdBuffers(bool enable);
|
||||
void setDumpShaders(bool enable);
|
||||
void setDumpPM4(bool enable);
|
||||
void setVblankDiv(u32 value);
|
||||
void setGpuId(s32 selectedGpuId);
|
||||
void setScreenWidth(u32 width);
|
||||
void setScreenHeight(u32 height);
|
||||
void setFullscreenMode(bool enable);
|
||||
void setPlayBGM(bool enable);
|
||||
void setBGMvolume(int volume);
|
||||
void setEnableDiscordRPC(bool enable);
|
||||
void setLanguage(u32 language);
|
||||
void setNeoMode(bool enable);
|
||||
void setUserName(const std::string& type);
|
||||
void setUpdateChannel(const std::string& type);
|
||||
void setSeparateUpdateEnabled(bool use);
|
||||
|
||||
void setCursorState(s16 cursorState);
|
||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||
void setBackButtonBehavior(const std::string& type);
|
||||
void setUseSpecialPad(bool use);
|
||||
void setSpecialPadClass(int type);
|
||||
|
||||
@@ -66,7 +86,9 @@ bool vkCrashDiagnosticEnabled();
|
||||
|
||||
// Gui
|
||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
|
||||
void setGameInstallDir(const std::string& dir);
|
||||
bool addGameInstallDir(const std::filesystem::path& dir);
|
||||
void removeGameInstallDir(const std::filesystem::path& dir);
|
||||
void setAddonInstallDir(const std::filesystem::path& dir);
|
||||
void setMainWindowTheme(u32 theme);
|
||||
void setIconSize(u32 size);
|
||||
void setIconSizeGrid(u32 size);
|
||||
@@ -84,7 +106,8 @@ u32 getMainWindowGeometryX();
|
||||
u32 getMainWindowGeometryY();
|
||||
u32 getMainWindowGeometryW();
|
||||
u32 getMainWindowGeometryH();
|
||||
std::string getGameInstallDir();
|
||||
const std::vector<std::filesystem::path>& getGameInstallDirs();
|
||||
std::filesystem::path getAddonInstallDir();
|
||||
u32 getMainWindowTheme();
|
||||
u32 getIconSize();
|
||||
u32 getIconSizeGrid();
|
||||
|
||||
@@ -81,34 +81,42 @@ public:
|
||||
return std::basic_string_view<T>{data};
|
||||
}
|
||||
|
||||
char* begin() {
|
||||
T* begin() {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const char* begin() const {
|
||||
const T* begin() const {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
char* end() {
|
||||
T* end() {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data + N;
|
||||
}
|
||||
|
||||
const char* end() const {
|
||||
const T* end() const {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data + N;
|
||||
}
|
||||
|
||||
constexpr std::size_t capacity() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return std::char_traits<T>::length(data);
|
||||
}
|
||||
|
||||
T& operator[](size_t idx) {
|
||||
return data[idx];
|
||||
}
|
||||
@@ -152,6 +160,12 @@ public:
|
||||
static_assert(sizeof(CString<13>) == sizeof(char[13])); // Ensure size still matches a simple array
|
||||
static_assert(std::weakly_incrementable<CString<13>::Iterator>);
|
||||
|
||||
template <size_t N>
|
||||
using CWString = CString<N, wchar_t>;
|
||||
|
||||
template <size_t N>
|
||||
using CU16String = CString<N, char16_t>;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace Common
|
||||
@@ -2,18 +2,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/disassembler.h"
|
||||
#include "common/decoder.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
Disassembler::Disassembler() {
|
||||
DecoderImpl::DecoderImpl() {
|
||||
ZydisDecoderInit(&m_decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
|
||||
ZydisFormatterInit(&m_formatter, ZYDIS_FORMATTER_STYLE_INTEL);
|
||||
}
|
||||
|
||||
Disassembler::~Disassembler() = default;
|
||||
DecoderImpl::~DecoderImpl() = default;
|
||||
|
||||
void Disassembler::printInstruction(void* code, u64 address) {
|
||||
void DecoderImpl::printInstruction(void* code, u64 address) {
|
||||
ZydisDecodedInstruction instruction;
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE];
|
||||
ZyanStatus status =
|
||||
@@ -25,7 +25,7 @@ void Disassembler::printInstruction(void* code, u64 address) {
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands,
|
||||
void DecoderImpl::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands,
|
||||
u64 address) {
|
||||
const int bufLen = 256;
|
||||
char szBuffer[bufLen];
|
||||
@@ -34,4 +34,9 @@ void Disassembler::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand*
|
||||
fmt::print("instruction: {}\n", szBuffer);
|
||||
}
|
||||
|
||||
ZyanStatus DecoderImpl::decodeInstruction(ZydisDecodedInstruction& inst,
|
||||
ZydisDecodedOperand* operands, void* data, u64 size) {
|
||||
return ZydisDecoderDecodeFull(&m_decoder, data, size, &inst, operands);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -4,21 +4,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Zydis/Zydis.h>
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class Disassembler {
|
||||
class DecoderImpl {
|
||||
public:
|
||||
Disassembler();
|
||||
~Disassembler();
|
||||
DecoderImpl();
|
||||
~DecoderImpl();
|
||||
|
||||
void printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands, u64 address);
|
||||
void printInstruction(void* code, u64 address);
|
||||
ZyanStatus decodeInstruction(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands,
|
||||
void* data, u64 size = 15);
|
||||
|
||||
private:
|
||||
ZydisDecoder m_decoder;
|
||||
ZydisFormatter m_formatter;
|
||||
};
|
||||
|
||||
using Decoder = Common::Singleton<DecoderImpl>;
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "common/discord.h"
|
||||
|
||||
namespace Discord {
|
||||
|
||||
void RPC::init() {
|
||||
DiscordEventHandlers handlers{};
|
||||
Discord_Initialize("1139939140494971051", &handlers, 1, nullptr);
|
||||
|
||||
startTimestamp = time(nullptr);
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void RPC::update(Discord::RPCStatus status, const std::string& game) {
|
||||
DiscordRichPresence rpc{};
|
||||
|
||||
if (status == Discord::RPCStatus::Playing) {
|
||||
rpc.details = "Playing a game";
|
||||
rpc.state = game.c_str();
|
||||
} else {
|
||||
rpc.details = "Idle";
|
||||
}
|
||||
|
||||
rpc.largeImageKey = "shadps4";
|
||||
rpc.largeImageText = "ShadPS4 is a PS4 emulator";
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::stop() {
|
||||
if (enabled) {
|
||||
enabled = false;
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Discord
|
||||
57
src/common/discord_rpc_handler.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "src/common/discord_rpc_handler.h"
|
||||
|
||||
namespace DiscordRPCHandler {
|
||||
|
||||
void RPC::init() {
|
||||
DiscordEventHandlers handlers{};
|
||||
|
||||
Discord_Initialize("1139939140494971051", &handlers, 1, nullptr);
|
||||
startTimestamp = time(nullptr);
|
||||
rpcEnabled = true;
|
||||
}
|
||||
|
||||
void RPC::setStatusIdling() {
|
||||
DiscordRichPresence rpc{};
|
||||
rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png";
|
||||
rpc.largeImageText = "shadPS4 is a PS4 emulator";
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
rpc.details = "Idle";
|
||||
|
||||
status = RPCStatus::Idling;
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::setStatusPlaying(const std::string& game_name, const std::string& game_id) {
|
||||
DiscordRichPresence rpc{};
|
||||
|
||||
rpc.details = "Playing";
|
||||
rpc.state = game_name.c_str();
|
||||
std::string largeImageUrl =
|
||||
"https://store.playstation.com/store/api/chihiro/00_09_000/titlecontainer/US/en/999/" +
|
||||
game_id + "_00/image";
|
||||
rpc.largeImageKey = largeImageUrl.c_str();
|
||||
rpc.largeImageText = game_name.c_str();
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
|
||||
status = RPCStatus::Playing;
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::shutdown() {
|
||||
if (rpcEnabled) {
|
||||
rpcEnabled = false;
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool RPC::getRPCEnabled() {
|
||||
return rpcEnabled;
|
||||
}
|
||||
|
||||
} // namespace DiscordRPCHandler
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
#include <discord_rpc.h>
|
||||
|
||||
namespace Discord {
|
||||
namespace DiscordRPCHandler {
|
||||
|
||||
enum class RPCStatus {
|
||||
Idling,
|
||||
@@ -16,12 +16,15 @@ enum class RPCStatus {
|
||||
|
||||
class RPC {
|
||||
std::uint64_t startTimestamp;
|
||||
bool enabled = false;
|
||||
bool rpcEnabled = false;
|
||||
RPCStatus status;
|
||||
|
||||
public:
|
||||
void init();
|
||||
void update(RPCStatus status, const std::string& title);
|
||||
void stop();
|
||||
void setStatusIdling();
|
||||
void setStatusPlaying(const std::string& game_name, const std::string& game_id);
|
||||
void shutdown();
|
||||
bool getRPCEnabled();
|
||||
};
|
||||
|
||||
} // namespace Discord
|
||||
} // namespace DiscordRPCHandler
|
||||
@@ -192,8 +192,9 @@ int IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileS
|
||||
#endif
|
||||
|
||||
if (!IsOpen()) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}",
|
||||
PathToUTF8String(file_path));
|
||||
const auto ec = std::error_code{result, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, error_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -230,7 +231,7 @@ void IOFile::Unlink() {
|
||||
|
||||
// Mark the file for deletion
|
||||
// TODO: Also remove the file path?
|
||||
#if _WIN64
|
||||
#ifdef _WIN64
|
||||
FILE_DISPOSITION_INFORMATION disposition;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
@@ -241,7 +242,11 @@ void IOFile::Unlink() {
|
||||
NtSetInformationFile(hfile, &iosb, &disposition, sizeof(disposition),
|
||||
FileDispositionInformation);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing Linux implementation");
|
||||
if (unlink(file_path.c_str()) != 0) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to unlink the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -372,6 +377,18 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 size = GetSize();
|
||||
if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) {
|
||||
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
|
||||
return false;
|
||||
} else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) {
|
||||
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
|
||||
return false;
|
||||
} else if (origin == SeekOrigin::End && offset > 0) {
|
||||
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
|
||||
|
||||
@@ -205,9 +205,9 @@ public:
|
||||
return WriteSpan(string);
|
||||
}
|
||||
|
||||
static void WriteBytes(const std::filesystem::path path, std::span<const u8> data) {
|
||||
static size_t WriteBytes(const std::filesystem::path path, std::span<const u8> data) {
|
||||
IOFile out(path, FileAccessMode::Write);
|
||||
out.Write(data);
|
||||
return out.Write(data);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -114,6 +114,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, AvPlayer) \
|
||||
SUB(Lib, Ngs2) \
|
||||
SUB(Lib, Audio3d) \
|
||||
SUB(Lib, Ime) \
|
||||
SUB(Lib, GameLiveStreaming) \
|
||||
SUB(Lib, Remoteplay) \
|
||||
SUB(Lib, SharePlay) \
|
||||
SUB(Lib, Fiber) \
|
||||
SUB(Lib, Vdec2) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
|
||||
@@ -19,3 +19,24 @@ struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace fmt {
|
||||
template <typename T = std::string_view>
|
||||
struct UTF {
|
||||
T data;
|
||||
|
||||
explicit UTF(const std::u8string_view view) {
|
||||
data = view.empty() ? T{} : T{(const char*)&view.front(), (const char*)&view.back() + 1};
|
||||
}
|
||||
|
||||
explicit UTF(const std::u8string& str) : UTF(std::u8string_view{str}) {}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<fmt::UTF<std::string_view>, char> : formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const UTF<std::string_view>& wrapper, FormatContext& ctx) const {
|
||||
return formatter<std::string_view>::format(wrapper.data, ctx);
|
||||
}
|
||||
};
|
||||
@@ -81,6 +81,12 @@ enum class Class : u8 {
|
||||
Lib_AvPlayer, ///< The LibSceAvPlayer implementation.
|
||||
Lib_Ngs2, ///< The LibSceNgs2 implementation.
|
||||
Lib_Audio3d, ///< The LibSceAudio3d implementation.
|
||||
Lib_Ime, ///< The LibSceIme implementation
|
||||
Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation
|
||||
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
|
||||
Lib_SharePlay, ///< The LibSceSharePlay implemenation
|
||||
Lib_Fiber, ///< The LibSceFiber implementation.
|
||||
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
|
||||
@@ -119,9 +119,9 @@ std::string convertValueToHex(const std::string type, const std::string valueStr
|
||||
void OnGameLoaded() {
|
||||
|
||||
if (!patchFile.empty()) {
|
||||
std::string patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string();
|
||||
std::filesystem::path patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir);
|
||||
|
||||
std::string filePath = patchDir + "/" + patchFile;
|
||||
auto filePath = (patchDir / patchFile).native();
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(filePath.c_str());
|
||||
@@ -187,8 +187,8 @@ void OnGameLoaded() {
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
|
||||
QString patchDir =
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string());
|
||||
QString patchDir;
|
||||
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
|
||||
QString repositories[] = {"GoldHEN", "shadPS4"};
|
||||
|
||||
for (const QString& repository : repositories) {
|
||||
|
||||
161
src/common/number_utils.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <half.hpp>
|
||||
|
||||
#include "common/number_utils.h"
|
||||
#include "video_core/amdgpu/pixel_format.h"
|
||||
#include "video_core/amdgpu/types.h"
|
||||
|
||||
#define UF11_EXPONENT_SHIFT 6
|
||||
#define UF10_EXPONENT_SHIFT 5
|
||||
|
||||
#define RGB9E5_MANTISSA_BITS 9
|
||||
#define RGB9E5_EXP_BIAS 1
|
||||
|
||||
#define F32_INFINITY 0x7f800000
|
||||
|
||||
namespace NumberUtils {
|
||||
|
||||
float Uf11ToF32(u16 val) {
|
||||
union {
|
||||
float f;
|
||||
u32 ui;
|
||||
} f32;
|
||||
|
||||
int exponent = (val & 0x07c0) >> UF11_EXPONENT_SHIFT;
|
||||
int mantissa = (val & 0x003f);
|
||||
|
||||
f32.f = 0.0;
|
||||
|
||||
if (exponent == 0) {
|
||||
if (mantissa != 0) {
|
||||
const float scale = 1.0 / (1 << 20);
|
||||
f32.f = scale * mantissa;
|
||||
}
|
||||
} else if (exponent == 31) {
|
||||
f32.ui = F32_INFINITY | mantissa;
|
||||
} else {
|
||||
float scale, decimal;
|
||||
exponent -= 15;
|
||||
if (exponent < 0) {
|
||||
scale = 1.0f / (1 << -exponent);
|
||||
} else {
|
||||
scale = (float)(1 << exponent);
|
||||
}
|
||||
decimal = 1.0f + (float)mantissa / 64;
|
||||
f32.f = scale * decimal;
|
||||
}
|
||||
|
||||
return f32.f;
|
||||
}
|
||||
|
||||
float Uf10ToF32(u16 val) {
|
||||
union {
|
||||
float f;
|
||||
u32 ui;
|
||||
} f32;
|
||||
|
||||
int exponent = (val & 0x03e0) >> UF10_EXPONENT_SHIFT;
|
||||
int mantissa = (val & 0x001f);
|
||||
|
||||
f32.f = 0.0;
|
||||
|
||||
if (exponent == 0) {
|
||||
if (mantissa != 0) {
|
||||
const float scale = 1.0 / (1 << 19);
|
||||
f32.f = scale * mantissa;
|
||||
}
|
||||
} else if (exponent == 31) {
|
||||
f32.ui = F32_INFINITY | mantissa;
|
||||
} else {
|
||||
float scale, decimal;
|
||||
exponent -= 15;
|
||||
if (exponent < 0) {
|
||||
scale = 1.0f / (1 << -exponent);
|
||||
} else {
|
||||
scale = (float)(1 << exponent);
|
||||
}
|
||||
decimal = 1.0f + (float)mantissa / 32;
|
||||
f32.f = scale * decimal;
|
||||
}
|
||||
|
||||
return f32.f;
|
||||
}
|
||||
|
||||
float Uf16ToF32(u16 val) {
|
||||
return half_float::half_cast<float>(reinterpret_cast<half_float::half&>(val));
|
||||
}
|
||||
|
||||
float U2ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 3.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S2ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 1.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U4ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 15.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S4ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 7.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U5ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 31.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S5ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 15.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U6ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 63.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S6ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 31.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U8ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 255.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S8ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 127.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U10ToUnorm(u16 val) {
|
||||
static constexpr auto c = 1.0f / 1023.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S10ToSnorm(s16 val) {
|
||||
static constexpr auto c = 1.0f / 511.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U16ToUnorm(u16 val) {
|
||||
static constexpr auto c = 1.0f / 65535.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S16ToSnorm(s16 val) {
|
||||
static constexpr auto c = 1.0f / 32767.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
} // namespace NumberUtils
|
||||
28
src/common/number_utils.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace NumberUtils {
|
||||
|
||||
float Uf11ToF32(u16 val);
|
||||
float Uf10ToF32(u16 val);
|
||||
float Uf16ToF32(u16 val);
|
||||
float U2ToUnorm(u8 val);
|
||||
float S2ToSnorm(s8 val);
|
||||
float U4ToUnorm(u8 val);
|
||||
float S4ToSnorm(s8 val);
|
||||
float U5ToUnorm(u8 val);
|
||||
float S5ToSnorm(s8 val);
|
||||
float U6ToUnorm(u8 val);
|
||||
float S6ToSnorm(s8 val);
|
||||
float U8ToUnorm(u8 val);
|
||||
float S8ToSnorm(s8 val);
|
||||
float U10ToUnorm(u16 val);
|
||||
float S10ToSnorm(s16 val);
|
||||
float U16ToUnorm(u16 val);
|
||||
float S16ToSnorm(s16 val);
|
||||
|
||||
} // namespace NumberUtils
|
||||
@@ -22,6 +22,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QString>
|
||||
#endif
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
@@ -82,18 +86,27 @@ static std::filesystem::path GetBundleParentDirectory() {
|
||||
|
||||
static auto UserPaths = [] {
|
||||
#ifdef __APPLE__
|
||||
// Start by assuming the base directory is the bundle's parent directory.
|
||||
std::filesystem::path base_dir = GetBundleParentDirectory();
|
||||
std::filesystem::path user_dir = base_dir / PORTABLE_DIR;
|
||||
// Check if the "user" directory exists in the current path:
|
||||
// Set the current path to the directory containing the app bundle.
|
||||
std::filesystem::current_path(GetBundleParentDirectory());
|
||||
#endif
|
||||
|
||||
// Try the portable user directory first.
|
||||
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
||||
if (!std::filesystem::exists(user_dir)) {
|
||||
// If it doesn't exist, use the new hardcoded path:
|
||||
// If it doesn't exist, use the standard path for the platform instead.
|
||||
// NOTE: On Windows we currently just create the portable directory instead.
|
||||
#ifdef __APPLE__
|
||||
user_dir =
|
||||
std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4";
|
||||
#elif defined(__linux__)
|
||||
const char* xdg_data_home = getenv("XDG_DATA_HOME");
|
||||
if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) {
|
||||
user_dir = std::filesystem::path(xdg_data_home) / "shadPS4";
|
||||
} else {
|
||||
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
|
||||
}
|
||||
#else
|
||||
const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unordered_map<PathType, fs::path> paths;
|
||||
|
||||
@@ -106,7 +119,6 @@ static auto UserPaths = [] {
|
||||
create_path(PathType::LogDir, user_dir / LOG_DIR);
|
||||
create_path(PathType::ScreenshotsDir, user_dir / SCREENSHOTS_DIR);
|
||||
create_path(PathType::ShaderDir, user_dir / SHADER_DIR);
|
||||
create_path(PathType::PM4Dir, user_dir / PM4_DIR);
|
||||
create_path(PathType::SaveDataDir, user_dir / SAVEDATA_DIR);
|
||||
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
||||
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
||||
@@ -115,7 +127,6 @@ static auto UserPaths = [] {
|
||||
create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR);
|
||||
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
||||
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
||||
create_path(PathType::AddonsDir, user_dir / ADDONS_DIR);
|
||||
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
||||
|
||||
return paths;
|
||||
@@ -165,4 +176,22 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) {
|
||||
UserPaths.insert_or_assign(shad_path, new_path);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
void PathToQString(QString& result, const std::filesystem::path& path) {
|
||||
#ifdef _WIN32
|
||||
result = QString::fromStdWString(path.wstring());
|
||||
#else
|
||||
result = QString::fromStdString(path.string());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::filesystem::path PathFromQString(const QString& path) {
|
||||
#ifdef _WIN32
|
||||
return std::filesystem::path(path.toStdWString());
|
||||
#else
|
||||
return std::filesystem::path(path.toStdString());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common::FS
|
||||
@@ -6,6 +6,10 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
class QString; // to avoid including <QString> in this header
|
||||
#endif
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
enum class PathType {
|
||||
@@ -13,7 +17,6 @@ enum class PathType {
|
||||
LogDir, // Where log files are stored.
|
||||
ScreenshotsDir, // Where screenshots are stored.
|
||||
ShaderDir, // Where shaders are stored.
|
||||
PM4Dir, // Where command lists are stored.
|
||||
SaveDataDir, // Where guest save data is stored.
|
||||
TempDataDir, // Where game temp data is stored.
|
||||
GameDataDir, // Where game data is stored.
|
||||
@@ -22,7 +25,6 @@ enum class PathType {
|
||||
CapturesDir, // Where rdoc captures are stored.
|
||||
CheatsDir, // Where cheats are stored.
|
||||
PatchesDir, // Where patches are stored.
|
||||
AddonsDir, // Where additional content is stored.
|
||||
MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored.
|
||||
};
|
||||
|
||||
@@ -32,7 +34,6 @@ constexpr auto PORTABLE_DIR = "user";
|
||||
constexpr auto LOG_DIR = "log";
|
||||
constexpr auto SCREENSHOTS_DIR = "screenshots";
|
||||
constexpr auto SHADER_DIR = "shader";
|
||||
constexpr auto PM4_DIR = "pm4";
|
||||
constexpr auto SAVEDATA_DIR = "savedata";
|
||||
constexpr auto GAMEDATA_DIR = "data";
|
||||
constexpr auto TEMPDATA_DIR = "temp";
|
||||
@@ -41,7 +42,6 @@ constexpr auto DOWNLOAD_DIR = "download";
|
||||
constexpr auto CAPTURES_DIR = "captures";
|
||||
constexpr auto CHEATS_DIR = "cheats";
|
||||
constexpr auto PATCHES_DIR = "patches";
|
||||
constexpr auto ADDONS_DIR = "addcont";
|
||||
constexpr auto METADATA_DIR = "game_data";
|
||||
|
||||
// Filenames
|
||||
@@ -96,4 +96,23 @@ constexpr auto LOG_FILE = "shad_log.txt";
|
||||
*/
|
||||
void SetUserPath(PathType user_path, const std::filesystem::path& new_path);
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
/**
|
||||
* Converts an std::filesystem::path to a QString.
|
||||
* The native underlying string of a path is wstring on Windows and string on POSIX.
|
||||
*
|
||||
* @param result The resulting QString
|
||||
* @param path The path to convert
|
||||
*/
|
||||
void PathToQString(QString& result, const std::filesystem::path& path);
|
||||
|
||||
/**
|
||||
* Converts a QString to an std::filesystem::path.
|
||||
* The native underlying string of a path is wstring on Windows and string on POSIX.
|
||||
*
|
||||
* @param path The path to convert
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path PathFromQString(const QString& path);
|
||||
#endif
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
#define GIT_REV "@GIT_REV@"
|
||||
#define GIT_BRANCH "@GIT_BRANCH@"
|
||||
#define GIT_DESC "@GIT_DESC@"
|
||||
#define BUILD_DATE "@BUILD_DATE@"
|
||||
|
||||
namespace Common {
|
||||
|
||||
const char g_scm_rev[] = GIT_REV;
|
||||
const char g_scm_branch[] = GIT_BRANCH;
|
||||
const char g_scm_desc[] = GIT_DESC;
|
||||
const char g_scm_date[] = BUILD_DATE;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -8,5 +8,6 @@ namespace Common {
|
||||
extern const char g_scm_rev[];
|
||||
extern const char g_scm_branch[];
|
||||
extern const char g_scm_desc[];
|
||||
extern const char g_scm_date[];
|
||||
|
||||
} // namespace Common
|
||||
|
||||
92
src/common/signal_context.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/signal_context.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/ucontext.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* GetXmmPointer(void* ctx, u8 index) {
|
||||
#if defined(_WIN32)
|
||||
#define CASE(index) \
|
||||
case index: \
|
||||
return (void*)(&((EXCEPTION_POINTERS*)ctx)->ContextRecord->Xmm##index.Low)
|
||||
#elif defined(__APPLE__)
|
||||
#define CASE(index) \
|
||||
case index: \
|
||||
return (void*)(&((ucontext_t*)ctx)->uc_mcontext->__fs.__fpu_xmm##index);
|
||||
#else
|
||||
#define CASE(index) \
|
||||
case index: \
|
||||
return (void*)(&((ucontext_t*)ctx)->uc_mcontext.fpregs->_xmm[index].element[0])
|
||||
#endif
|
||||
switch (index) {
|
||||
CASE(0);
|
||||
CASE(1);
|
||||
CASE(2);
|
||||
CASE(3);
|
||||
CASE(4);
|
||||
CASE(5);
|
||||
CASE(6);
|
||||
CASE(7);
|
||||
CASE(8);
|
||||
CASE(9);
|
||||
CASE(10);
|
||||
CASE(11);
|
||||
CASE(12);
|
||||
CASE(13);
|
||||
CASE(14);
|
||||
CASE(15);
|
||||
default: {
|
||||
UNREACHABLE_MSG("Invalid XMM register index: {}", index);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#undef CASE
|
||||
}
|
||||
|
||||
void* GetRip(void* ctx) {
|
||||
#if defined(_WIN32)
|
||||
return (void*)((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip;
|
||||
#elif defined(__APPLE__)
|
||||
return (void*)((ucontext_t*)ctx)->uc_mcontext->__ss.__rip;
|
||||
#else
|
||||
return (void*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP];
|
||||
#endif
|
||||
}
|
||||
|
||||
void IncrementRip(void* ctx, u64 length) {
|
||||
#if defined(_WIN32)
|
||||
((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip += length;
|
||||
#elif defined(__APPLE__)
|
||||
((ucontext_t*)ctx)->uc_mcontext->__ss.__rip += length;
|
||||
#else
|
||||
((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP] += length;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsWriteError(void* ctx) {
|
||||
#if defined(_WIN32)
|
||||
return ((EXCEPTION_POINTERS*)ctx)->ExceptionRecord->ExceptionInformation[0] == 1;
|
||||
#elif defined(__APPLE__)
|
||||
#if defined(ARCH_X86_64)
|
||||
return ((ucontext_t*)ctx)->uc_mcontext->__es.__err & 0x2;
|
||||
#elif defined(ARCH_ARM64)
|
||||
return ((ucontext_t*)ctx)->uc_mcontext->__es.__esr & 0x40;
|
||||
#endif
|
||||
#else
|
||||
#if defined(ARCH_X86_64)
|
||||
return ((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ERR] & 0x2;
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
} // namespace Common
|
||||
18
src/common/signal_context.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* GetXmmPointer(void* ctx, u8 index);
|
||||
|
||||
void* GetRip(void* ctx);
|
||||
|
||||
void IncrementRip(void* ctx, u64 length);
|
||||
|
||||
bool IsWriteError(void* ctx);
|
||||
|
||||
} // namespace Common
|
||||
@@ -3,10 +3,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "ntapi.h"
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
@@ -102,6 +104,16 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
SetThreadPriority(handle, windows_priority);
|
||||
}
|
||||
|
||||
static void AccurateSleep(std::chrono::nanoseconds duration) {
|
||||
LARGE_INTEGER interval{
|
||||
.QuadPart = -1 * (duration.count() / 100u),
|
||||
};
|
||||
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
::CloseHandle(timer);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
@@ -122,6 +134,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
|
||||
}
|
||||
|
||||
static void AccurateSleep(std::chrono::nanoseconds duration) {
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -164,4 +180,22 @@ void SetCurrentThreadName(const char*) {
|
||||
|
||||
#endif
|
||||
|
||||
AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
|
||||
: target_interval(target_interval) {}
|
||||
|
||||
void AccurateTimer::Start() {
|
||||
auto begin_sleep = std::chrono::high_resolution_clock::now();
|
||||
if (total_wait.count() > 0) {
|
||||
AccurateSleep(total_wait);
|
||||
}
|
||||
start_time = std::chrono::high_resolution_clock::now();
|
||||
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);
|
||||
}
|
||||
|
||||
void AccurateTimer::End() {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
total_wait +=
|
||||
target_interval - std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -23,4 +23,18 @@ void SetCurrentThreadPriority(ThreadPriority new_priority);
|
||||
|
||||
void SetCurrentThreadName(const char* name);
|
||||
|
||||
class AccurateTimer {
|
||||
std::chrono::nanoseconds target_interval{};
|
||||
std::chrono::nanoseconds total_wait{};
|
||||
|
||||
std::chrono::high_resolution_clock::time_point start_time;
|
||||
|
||||
public:
|
||||
explicit AccurateTimer(std::chrono::nanoseconds target_interval);
|
||||
|
||||
void Start();
|
||||
|
||||
void End();
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr char VERSION[] = "0.3.0";
|
||||
constexpr char VERSION[] = "0.4.0";
|
||||
constexpr bool isRelease = true;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -25,19 +25,18 @@ asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000");
|
||||
|
||||
namespace Core {
|
||||
|
||||
static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE;
|
||||
static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
[[nodiscard]] constexpr u64 ToWindowsProt(Core::MemoryProt prot) {
|
||||
switch (prot) {
|
||||
case Core::MemoryProt::NoAccess:
|
||||
default:
|
||||
return PAGE_NOACCESS;
|
||||
case Core::MemoryProt::CpuRead:
|
||||
return PAGE_READONLY;
|
||||
case Core::MemoryProt::CpuReadWrite:
|
||||
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
|
||||
True(prot & Core::MemoryProt::GpuReadWrite)) {
|
||||
return PAGE_READWRITE;
|
||||
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
|
||||
return PAGE_READONLY;
|
||||
} else {
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +71,8 @@ struct AddressSpace::Impl {
|
||||
}
|
||||
reduction += ReductionOnFail;
|
||||
}
|
||||
ASSERT_MSG(virtual_base, "Unable to reserve virtual address space!");
|
||||
ASSERT_MSG(virtual_base, "Unable to reserve virtual address space: {}",
|
||||
Common::GetLastErrorMsg());
|
||||
|
||||
// Take the reduction off of the system managed area, and leave the others unchanged.
|
||||
system_managed_base = virtual_base;
|
||||
@@ -289,14 +289,13 @@ enum PosixPageProtection {
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr PosixPageProtection ToPosixProt(Core::MemoryProt prot) {
|
||||
switch (prot) {
|
||||
case Core::MemoryProt::NoAccess:
|
||||
default:
|
||||
return PAGE_NOACCESS;
|
||||
case Core::MemoryProt::CpuRead:
|
||||
return PAGE_READONLY;
|
||||
case Core::MemoryProt::CpuReadWrite:
|
||||
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
|
||||
True(prot & Core::MemoryProt::GpuReadWrite)) {
|
||||
return PAGE_READWRITE;
|
||||
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
|
||||
return PAGE_READONLY;
|
||||
} else {
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ public:
|
||||
explicit AddressSpace();
|
||||
~AddressSpace();
|
||||
|
||||
[[nodiscard]] u8* BackingBase() const noexcept {
|
||||
return backing_base;
|
||||
}
|
||||
|
||||
[[nodiscard]] VAddr SystemManagedVirtualBase() noexcept {
|
||||
return reinterpret_cast<VAddr>(system_managed_base);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
#include <set>
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/decoder.h"
|
||||
#include "common/signal_context.h"
|
||||
#include "common/types.h"
|
||||
#include "core/signals.h"
|
||||
#include "core/tls.h"
|
||||
@@ -26,6 +30,16 @@
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
#define MAYBE_AVX(OPCODE, ...) \
|
||||
[&] { \
|
||||
Cpu cpu; \
|
||||
if (cpu.has(Cpu::tAVX)) { \
|
||||
c.v##OPCODE(__VA_ARGS__); \
|
||||
} else { \
|
||||
c.OPCODE(__VA_ARGS__); \
|
||||
} \
|
||||
}()
|
||||
|
||||
namespace Core {
|
||||
|
||||
static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) {
|
||||
@@ -586,6 +600,273 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
|
||||
Cpu cpu;
|
||||
return !cpu.has(Cpu::tSSE4a);
|
||||
}
|
||||
|
||||
static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||
|
||||
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER, "operand 0 must be a register");
|
||||
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
|
||||
ASSERT_MSG(dst.isXMM(), "operand 0 must be an XMM register");
|
||||
|
||||
Xbyak::Xmm xmm_dst = *reinterpret_cast<const Xbyak::Xmm*>(&dst);
|
||||
|
||||
if (immediateForm) {
|
||||
u8 length = operands[1].imm.value.u & 0x3F;
|
||||
u8 index = operands[2].imm.value.u & 0x3F;
|
||||
|
||||
LOG_DEBUG(Core, "Patching immediate form EXTRQ, length: {}, index: {}", length, index);
|
||||
|
||||
const Xbyak::Reg64 scratch1 = rax;
|
||||
const Xbyak::Reg64 scratch2 = rcx;
|
||||
|
||||
// Set rsp to before red zone and save scratch registers
|
||||
c.lea(rsp, ptr[rsp - 128]);
|
||||
c.pushfq();
|
||||
c.push(scratch1);
|
||||
c.push(scratch2);
|
||||
|
||||
u64 mask;
|
||||
if (length == 0) {
|
||||
length = 64; // for the check below
|
||||
mask = 0xFFFF'FFFF'FFFF'FFFF;
|
||||
} else {
|
||||
mask = (1ULL << length) - 1;
|
||||
}
|
||||
|
||||
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
|
||||
|
||||
// Get lower qword from xmm register
|
||||
MAYBE_AVX(movq, scratch1, xmm_dst);
|
||||
|
||||
if (index != 0) {
|
||||
c.shr(scratch1, index);
|
||||
}
|
||||
|
||||
// We need to move mask to a register because we can't use all the possible
|
||||
// immediate values with `and reg, imm32`
|
||||
c.mov(scratch2, mask);
|
||||
c.and_(scratch1, scratch2);
|
||||
|
||||
// Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't
|
||||
// care to preserve them
|
||||
MAYBE_AVX(movq, xmm_dst, scratch1);
|
||||
|
||||
c.pop(scratch2);
|
||||
c.pop(scratch1);
|
||||
c.popfq();
|
||||
c.lea(rsp, ptr[rsp + 128]);
|
||||
} else {
|
||||
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[0].reg.value >= ZYDIS_REGISTER_XMM0 &&
|
||||
operands[0].reg.value <= ZYDIS_REGISTER_XMM15 &&
|
||||
operands[1].reg.value >= ZYDIS_REGISTER_XMM0 &&
|
||||
operands[1].reg.value <= ZYDIS_REGISTER_XMM15,
|
||||
"Unexpected operand types for EXTRQ instruction");
|
||||
|
||||
const auto src = ZydisToXbyakRegisterOperand(operands[1]);
|
||||
|
||||
ASSERT_MSG(src.isXMM(), "operand 1 must be an XMM register");
|
||||
|
||||
Xbyak::Xmm xmm_src = *reinterpret_cast<const Xbyak::Xmm*>(&src);
|
||||
|
||||
const Xbyak::Reg64 scratch1 = rax;
|
||||
const Xbyak::Reg64 scratch2 = rcx;
|
||||
const Xbyak::Reg64 mask = rdx;
|
||||
|
||||
Xbyak::Label length_zero, end;
|
||||
|
||||
c.lea(rsp, ptr[rsp - 128]);
|
||||
c.pushfq();
|
||||
c.push(scratch1);
|
||||
c.push(scratch2);
|
||||
c.push(mask);
|
||||
|
||||
// Construct the mask out of the length that resides in bottom 6 bits of source xmm
|
||||
MAYBE_AVX(movq, scratch1, xmm_src);
|
||||
c.mov(scratch2, scratch1);
|
||||
c.and_(scratch2, 0x3F);
|
||||
c.jz(length_zero);
|
||||
|
||||
// mask = (1ULL << length) - 1
|
||||
c.mov(mask, 1);
|
||||
c.shl(mask, cl);
|
||||
c.dec(mask);
|
||||
c.jmp(end);
|
||||
|
||||
c.L(length_zero);
|
||||
c.mov(mask, 0xFFFF'FFFF'FFFF'FFFF);
|
||||
|
||||
c.L(end);
|
||||
|
||||
// Get the shift amount and store it in scratch2
|
||||
c.shr(scratch1, 8);
|
||||
c.and_(scratch1, 0x3F);
|
||||
c.mov(scratch2, scratch1); // cl now contains the shift amount
|
||||
|
||||
MAYBE_AVX(movq, scratch1, xmm_dst);
|
||||
c.shr(scratch1, cl);
|
||||
c.and_(scratch1, mask);
|
||||
MAYBE_AVX(movq, xmm_dst, scratch1);
|
||||
|
||||
c.pop(mask);
|
||||
c.pop(scratch2);
|
||||
c.pop(scratch1);
|
||||
c.popfq();
|
||||
c.lea(rsp, ptr[rsp + 128]);
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||
|
||||
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER,
|
||||
"operands 0 and 1 must be registers.");
|
||||
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakRegisterOperand(operands[1]);
|
||||
|
||||
ASSERT_MSG(dst.isXMM() && src.isXMM(), "operands 0 and 1 must be xmm registers.");
|
||||
|
||||
Xbyak::Xmm xmm_dst = *reinterpret_cast<const Xbyak::Xmm*>(&dst);
|
||||
Xbyak::Xmm xmm_src = *reinterpret_cast<const Xbyak::Xmm*>(&src);
|
||||
|
||||
if (immediateForm) {
|
||||
u8 length = operands[2].imm.value.u & 0x3F;
|
||||
u8 index = operands[3].imm.value.u & 0x3F;
|
||||
|
||||
const Xbyak::Reg64 scratch1 = rax;
|
||||
const Xbyak::Reg64 scratch2 = rcx;
|
||||
const Xbyak::Reg64 mask = rdx;
|
||||
|
||||
// Set rsp to before red zone and save scratch registers
|
||||
c.lea(rsp, ptr[rsp - 128]);
|
||||
c.pushfq();
|
||||
c.push(scratch1);
|
||||
c.push(scratch2);
|
||||
c.push(mask);
|
||||
|
||||
u64 mask_value;
|
||||
if (length == 0) {
|
||||
length = 64; // for the check below
|
||||
mask_value = 0xFFFF'FFFF'FFFF'FFFF;
|
||||
} else {
|
||||
mask_value = (1ULL << length) - 1;
|
||||
}
|
||||
|
||||
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
|
||||
|
||||
MAYBE_AVX(movq, scratch1, xmm_src);
|
||||
MAYBE_AVX(movq, scratch2, xmm_dst);
|
||||
c.mov(mask, mask_value);
|
||||
|
||||
// src &= mask
|
||||
c.and_(scratch1, mask);
|
||||
|
||||
// src <<= index
|
||||
c.shl(scratch1, index);
|
||||
|
||||
// dst &= ~(mask << index)
|
||||
mask_value = ~(mask_value << index);
|
||||
c.mov(mask, mask_value);
|
||||
c.and_(scratch2, mask);
|
||||
|
||||
// dst |= src
|
||||
c.or_(scratch2, scratch1);
|
||||
|
||||
// Insert scratch2 into low 64 bits of dst, upper 64 bits are unaffected
|
||||
Cpu cpu;
|
||||
if (cpu.has(Cpu::tAVX)) {
|
||||
c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0);
|
||||
} else {
|
||||
c.pinsrq(xmm_dst, scratch2, 0);
|
||||
}
|
||||
|
||||
c.pop(mask);
|
||||
c.pop(scratch2);
|
||||
c.pop(scratch1);
|
||||
c.popfq();
|
||||
c.lea(rsp, ptr[rsp + 128]);
|
||||
} else {
|
||||
ASSERT_MSG(operands[2].type == ZYDIS_OPERAND_TYPE_UNUSED &&
|
||||
operands[3].type == ZYDIS_OPERAND_TYPE_UNUSED,
|
||||
"operands 2 and 3 must be unused for register form.");
|
||||
|
||||
const Xbyak::Reg64 scratch1 = rax;
|
||||
const Xbyak::Reg64 scratch2 = rcx;
|
||||
const Xbyak::Reg64 index = rdx;
|
||||
const Xbyak::Reg64 mask = rbx;
|
||||
|
||||
Xbyak::Label length_zero, end;
|
||||
|
||||
c.lea(rsp, ptr[rsp - 128]);
|
||||
c.pushfq();
|
||||
c.push(scratch1);
|
||||
c.push(scratch2);
|
||||
c.push(index);
|
||||
c.push(mask);
|
||||
|
||||
// Get upper 64 bits of src and copy it to mask and index
|
||||
MAYBE_AVX(pextrq, index, xmm_src, 1);
|
||||
c.mov(mask, index);
|
||||
|
||||
// When length is 0, set it to 64
|
||||
c.and_(mask, 0x3F); // mask now holds the length
|
||||
c.jz(length_zero); // Check if length is 0 and set mask to all 1s if it is
|
||||
|
||||
// Create a mask out of the length
|
||||
c.mov(cl, mask.cvt8());
|
||||
c.mov(mask, 1);
|
||||
c.shl(mask, cl);
|
||||
c.dec(mask);
|
||||
c.jmp(end);
|
||||
|
||||
c.L(length_zero);
|
||||
c.mov(mask, 0xFFFF'FFFF'FFFF'FFFF);
|
||||
|
||||
c.L(end);
|
||||
// Get index to insert at
|
||||
c.shr(index, 8);
|
||||
c.and_(index, 0x3F);
|
||||
|
||||
// src &= mask
|
||||
MAYBE_AVX(movq, scratch1, xmm_src);
|
||||
c.and_(scratch1, mask);
|
||||
|
||||
// mask = ~(mask << index)
|
||||
c.mov(cl, index.cvt8());
|
||||
c.shl(mask, cl);
|
||||
c.not_(mask);
|
||||
|
||||
// src <<= index
|
||||
c.shl(scratch1, cl);
|
||||
|
||||
// dst = (dst & mask) | src
|
||||
MAYBE_AVX(movq, scratch2, xmm_dst);
|
||||
c.and_(scratch2, mask);
|
||||
c.or_(scratch2, scratch1);
|
||||
|
||||
// Upper 64 bits are undefined in insertq
|
||||
MAYBE_AVX(movq, xmm_dst, scratch2);
|
||||
|
||||
c.pop(mask);
|
||||
c.pop(index);
|
||||
c.pop(scratch2);
|
||||
c.pop(scratch1);
|
||||
c.popfq();
|
||||
c.lea(rsp, ptr[rsp + 128]);
|
||||
}
|
||||
}
|
||||
|
||||
using PatchFilter = bool (*)(const ZydisDecodedOperand*);
|
||||
using InstructionGenerator = void (*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
|
||||
struct PatchInfo {
|
||||
@@ -607,6 +888,9 @@ static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
|
||||
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}},
|
||||
#endif
|
||||
|
||||
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
|
||||
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Patches for instruction sets not supported by Rosetta 2.
|
||||
// BMI1
|
||||
@@ -622,7 +906,6 @@ static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
|
||||
};
|
||||
|
||||
static std::once_flag init_flag;
|
||||
static ZydisDecoder instr_decoder;
|
||||
|
||||
struct PatchModule {
|
||||
/// Mutex controlling access to module code regions.
|
||||
@@ -663,22 +946,31 @@ static PatchModule* GetModule(const void* ptr) {
|
||||
static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
|
||||
ZydisDecodedInstruction instruction;
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||
const auto status =
|
||||
ZydisDecoderDecodeFull(&instr_decoder, code, module->end - code, &instruction, operands);
|
||||
const auto status = Common::Decoder::Instance()->decodeInstruction(instruction, operands, code,
|
||||
module->end - code);
|
||||
if (!ZYAN_SUCCESS(status)) {
|
||||
return std::make_pair(false, 1);
|
||||
}
|
||||
|
||||
if (Patches.contains(instruction.mnemonic)) {
|
||||
const auto& patch_info = Patches.at(instruction.mnemonic);
|
||||
bool needs_trampoline = patch_info.trampoline;
|
||||
if (patch_info.filter(operands)) {
|
||||
auto& patch_gen = module->patch_gen;
|
||||
|
||||
if (needs_trampoline && instruction.length < 5) {
|
||||
// Trampoline is needed but instruction is too short to patch.
|
||||
// Return false and length to fall back to the illegal instruction handler,
|
||||
// or to signal to AOT compilation that this instruction should be skipped and
|
||||
// handled at runtime.
|
||||
return std::make_pair(false, instruction.length);
|
||||
}
|
||||
|
||||
// Reset state and move to current code position.
|
||||
patch_gen.reset();
|
||||
patch_gen.setSize(code - patch_gen.getCode());
|
||||
|
||||
if (patch_info.trampoline) {
|
||||
if (needs_trampoline) {
|
||||
auto& trampoline_gen = module->trampoline_gen;
|
||||
const auto trampoline_ptr = trampoline_gen.getCurr();
|
||||
|
||||
@@ -714,6 +1006,153 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
|
||||
return std::make_pair(false, instruction.length);
|
||||
}
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
|
||||
static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
|
||||
ZydisDecodedInstruction instruction;
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||
const auto status =
|
||||
Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address);
|
||||
|
||||
switch (instruction.mnemonic) {
|
||||
case ZYDIS_MNEMONIC_EXTRQ: {
|
||||
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||
if (immediateForm) {
|
||||
LOG_CRITICAL(Core, "EXTRQ immediate form should have been patched at code address: {}",
|
||||
fmt::ptr(code_address));
|
||||
return false;
|
||||
} else {
|
||||
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[0].reg.value >= ZYDIS_REGISTER_XMM0 &&
|
||||
operands[0].reg.value <= ZYDIS_REGISTER_XMM15 &&
|
||||
operands[1].reg.value >= ZYDIS_REGISTER_XMM0 &&
|
||||
operands[1].reg.value <= ZYDIS_REGISTER_XMM15,
|
||||
"Unexpected operand types for EXTRQ instruction");
|
||||
|
||||
const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0;
|
||||
const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0;
|
||||
|
||||
const auto dst = Common::GetXmmPointer(ctx, dstIndex);
|
||||
const auto src = Common::GetXmmPointer(ctx, srcIndex);
|
||||
|
||||
u64 lowQWordSrc;
|
||||
memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc));
|
||||
|
||||
u64 lowQWordDst;
|
||||
memcpy(&lowQWordDst, dst, sizeof(lowQWordDst));
|
||||
|
||||
u64 length = lowQWordSrc & 0x3F;
|
||||
u64 mask;
|
||||
if (length == 0) {
|
||||
length = 64; // for the check below
|
||||
mask = 0xFFFF'FFFF'FFFF'FFFF;
|
||||
} else {
|
||||
mask = (1ULL << length) - 1;
|
||||
}
|
||||
|
||||
u64 index = (lowQWordSrc >> 8) & 0x3F;
|
||||
if (length + index > 64) {
|
||||
// Undefined behavior if length + index is bigger than 64 according to the spec,
|
||||
// we'll warn and continue execution.
|
||||
LOG_WARNING(Core,
|
||||
"extrq at {} with length {} and index {} is bigger than 64, "
|
||||
"undefined behavior",
|
||||
fmt::ptr(code_address), length, index);
|
||||
}
|
||||
|
||||
lowQWordDst >>= index;
|
||||
lowQWordDst &= mask;
|
||||
|
||||
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
|
||||
|
||||
Common::IncrementRip(ctx, instruction.length);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZYDIS_MNEMONIC_INSERTQ: {
|
||||
bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||
if (immediateForm) {
|
||||
LOG_CRITICAL(Core,
|
||||
"INSERTQ immediate form should have been patched at code address: {}",
|
||||
fmt::ptr(code_address));
|
||||
return false;
|
||||
} else {
|
||||
ASSERT_MSG(operands[2].type == ZYDIS_OPERAND_TYPE_UNUSED &&
|
||||
operands[3].type == ZYDIS_OPERAND_TYPE_UNUSED,
|
||||
"operands 2 and 3 must be unused for register form.");
|
||||
|
||||
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER,
|
||||
"operands 0 and 1 must be registers.");
|
||||
|
||||
const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0;
|
||||
const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0;
|
||||
|
||||
const auto dst = Common::GetXmmPointer(ctx, dstIndex);
|
||||
const auto src = Common::GetXmmPointer(ctx, srcIndex);
|
||||
|
||||
u64 lowQWordSrc, highQWordSrc;
|
||||
memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc));
|
||||
memcpy(&highQWordSrc, (u8*)src + 8, sizeof(highQWordSrc));
|
||||
|
||||
u64 lowQWordDst;
|
||||
memcpy(&lowQWordDst, dst, sizeof(lowQWordDst));
|
||||
|
||||
u64 length = highQWordSrc & 0x3F;
|
||||
u64 mask;
|
||||
if (length == 0) {
|
||||
length = 64; // for the check below
|
||||
mask = 0xFFFF'FFFF'FFFF'FFFF;
|
||||
} else {
|
||||
mask = (1ULL << length) - 1;
|
||||
}
|
||||
|
||||
u64 index = (highQWordSrc >> 8) & 0x3F;
|
||||
if (length + index > 64) {
|
||||
// Undefined behavior if length + index is bigger than 64 according to the spec,
|
||||
// we'll warn and continue execution.
|
||||
LOG_WARNING(Core,
|
||||
"insertq at {} with length {} and index {} is bigger than 64, "
|
||||
"undefined behavior",
|
||||
fmt::ptr(code_address), length, index);
|
||||
}
|
||||
|
||||
lowQWordSrc &= mask;
|
||||
lowQWordDst &= ~(mask << index);
|
||||
lowQWordDst |= lowQWordSrc << index;
|
||||
|
||||
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
|
||||
|
||||
Common::IncrementRip(ctx, instruction.length);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}",
|
||||
fmt::ptr(code_address), ZydisMnemonicGetString(instruction.mnemonic));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
#elif defined(ARCH_ARM64)
|
||||
// These functions shouldn't be needed for ARM as it will use a JIT so there's no need to patch
|
||||
// instructions.
|
||||
static bool TryExecuteIllegalInstruction(void*, void*) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
static bool TryPatchJit(void* code_address) {
|
||||
auto* code = static_cast<u8*>(code_address);
|
||||
auto* module = GetModule(code);
|
||||
@@ -746,17 +1185,19 @@ static void TryPatchAot(void* code_address, u64 code_size) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool PatchesAccessViolationHandler(void* code_address, void* fault_address, bool is_write) {
|
||||
return TryPatchJit(code_address);
|
||||
static bool PatchesAccessViolationHandler(void* context, void* /* fault_address */) {
|
||||
return TryPatchJit(Common::GetRip(context));
|
||||
}
|
||||
|
||||
static bool PatchesIllegalInstructionHandler(void* code_address) {
|
||||
return TryPatchJit(code_address);
|
||||
static bool PatchesIllegalInstructionHandler(void* context) {
|
||||
void* code_address = Common::GetRip(context);
|
||||
if (!TryPatchJit(code_address)) {
|
||||
return TryExecuteIllegalInstruction(context, code_address);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PatchesInit() {
|
||||
ZydisDecoderInit(&instr_decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
|
||||
|
||||
if (!Patches.empty()) {
|
||||
auto* signals = Signals::Instance();
|
||||
// Should be called last.
|
||||
|
||||
178
src/core/debug_state.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/native_clock.h"
|
||||
#include "common/singleton.h"
|
||||
#include "debug_state.h"
|
||||
#include "devtools/widget/common.h"
|
||||
#include "libraries/kernel/time_management.h"
|
||||
#include "libraries/system/msgdialog.h"
|
||||
#include "video_core/amdgpu/pm4_cmds.h"
|
||||
|
||||
using namespace DebugStateType;
|
||||
|
||||
DebugStateImpl& DebugState = *Common::Singleton<DebugStateImpl>::Instance();
|
||||
|
||||
static ThreadID ThisThreadID() {
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PauseThread(ThreadID id) {
|
||||
#ifdef _WIN32
|
||||
auto handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
|
||||
SuspendThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ResumeThread(ThreadID id) {
|
||||
#ifdef _WIN32
|
||||
auto handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
|
||||
ResumeThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DebugStateImpl::AddCurrentThreadToGuestList() {
|
||||
std::lock_guard lock{guest_threads_mutex};
|
||||
const ThreadID id = ThisThreadID();
|
||||
guest_threads.push_back(id);
|
||||
}
|
||||
|
||||
void DebugStateImpl::RemoveCurrentThreadFromGuestList() {
|
||||
std::lock_guard lock{guest_threads_mutex};
|
||||
const ThreadID id = ThisThreadID();
|
||||
std::erase_if(guest_threads, [&](const ThreadID& v) { return v == id; });
|
||||
}
|
||||
|
||||
void DebugStateImpl::PauseGuestThreads() {
|
||||
using namespace Libraries::MsgDialog;
|
||||
std::unique_lock lock{guest_threads_mutex};
|
||||
if (is_guest_threads_paused) {
|
||||
return;
|
||||
}
|
||||
if (ShouldPauseInSubmit()) {
|
||||
waiting_submit_pause = false;
|
||||
should_show_frame_dump = true;
|
||||
}
|
||||
bool self_guest = false;
|
||||
ThreadID self_id = ThisThreadID();
|
||||
for (const auto& id : guest_threads) {
|
||||
if (id == self_id) {
|
||||
self_guest = true;
|
||||
} else {
|
||||
PauseThread(id);
|
||||
}
|
||||
}
|
||||
pause_time = Libraries::Kernel::Dev::GetClock()->GetUptime();
|
||||
is_guest_threads_paused = true;
|
||||
lock.unlock();
|
||||
if (self_guest) {
|
||||
PauseThread(self_id);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugStateImpl::ResumeGuestThreads() {
|
||||
std::lock_guard lock{guest_threads_mutex};
|
||||
if (!is_guest_threads_paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 delta_time = Libraries::Kernel::Dev::GetClock()->GetUptime() - pause_time;
|
||||
Libraries::Kernel::Dev::GetInitialPtc() += delta_time;
|
||||
for (const auto& id : guest_threads) {
|
||||
ResumeThread(id);
|
||||
}
|
||||
is_guest_threads_paused = false;
|
||||
}
|
||||
|
||||
void DebugStateImpl::RequestFrameDump(s32 count) {
|
||||
ASSERT(!DumpingCurrentFrame());
|
||||
gnm_frame_dump_request_count = count;
|
||||
frame_dump_list.clear();
|
||||
frame_dump_list.resize(count);
|
||||
const auto f = gnm_frame_count.load() + 1;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
frame_dump_list[i].frame_id = f + i;
|
||||
}
|
||||
waiting_submit_pause = true;
|
||||
}
|
||||
|
||||
void DebugStateImpl::PushQueueDump(QueueDump dump) {
|
||||
ASSERT(DumpingCurrentFrame());
|
||||
std::unique_lock lock{frame_dump_list_mutex};
|
||||
auto& frame = GetFrameDump();
|
||||
{ // Find draw calls
|
||||
auto data = std::span{dump.data};
|
||||
auto initial_data = data.data();
|
||||
while (!data.empty()) {
|
||||
const auto* header = reinterpret_cast<const AmdGpu::PM4Type3Header*>(data.data());
|
||||
const auto type = header->type;
|
||||
if (type == 2) {
|
||||
data = data.subspan(1);
|
||||
} else if (type != 3) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
const AmdGpu::PM4ItOpcode opcode = header->opcode;
|
||||
if (Core::Devtools::Widget::IsDrawCall(opcode)) {
|
||||
const auto offset =
|
||||
reinterpret_cast<uintptr_t>(header) - reinterpret_cast<uintptr_t>(initial_data);
|
||||
const auto addr = dump.base_addr + offset;
|
||||
waiting_reg_dumps.emplace(addr, &frame);
|
||||
waiting_reg_dumps_dbg.emplace(
|
||||
addr,
|
||||
fmt::format("#{} h({}) queue {} {} {}",
|
||||
frame_dump_list.size() - gnm_frame_dump_request_count, addr,
|
||||
magic_enum::enum_name(dump.type), dump.submit_num, dump.num2));
|
||||
}
|
||||
data = data.subspan(header->NumWords() + 1);
|
||||
}
|
||||
}
|
||||
frame.queues.push_back(std::move(dump));
|
||||
}
|
||||
|
||||
void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
const AmdGpu::Liverpool::Regs& regs, bool is_compute) {
|
||||
std::scoped_lock lock{frame_dump_list_mutex};
|
||||
const auto it = waiting_reg_dumps.find(header_addr);
|
||||
if (it == waiting_reg_dumps.end()) {
|
||||
return;
|
||||
}
|
||||
auto& frame = *it->second;
|
||||
waiting_reg_dumps.erase(it);
|
||||
waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr));
|
||||
auto& dump = frame.regs[header_addr - base_addr];
|
||||
dump.regs = regs;
|
||||
if (is_compute) {
|
||||
dump.is_compute = true;
|
||||
const auto& cs = dump.regs.cs_program;
|
||||
dump.cs_data = ComputerShaderDump{
|
||||
.cs_program = cs,
|
||||
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
|
||||
};
|
||||
} else {
|
||||
for (int i = 0; i < RegDump::MaxShaderStages; i++) {
|
||||
if (regs.stage_enable.IsStageEnabled(i)) {
|
||||
auto stage = regs.ProgramForStage(i);
|
||||
if (stage->address_lo != 0) {
|
||||
auto code = stage->Code();
|
||||
dump.stages[i] = ShaderDump{
|
||||
.user_data = *stage,
|
||||
.code = std::vector<u32>{code.begin(), code.end()},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
src/core/debug_state.h
Normal file
@@ -0,0 +1,158 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
using ThreadID = DWORD;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
using ThreadID = pthread_t;
|
||||
#endif
|
||||
|
||||
namespace Core::Devtools {
|
||||
class Layer;
|
||||
namespace Widget {
|
||||
class FrameGraph;
|
||||
}
|
||||
} // namespace Core::Devtools
|
||||
|
||||
namespace DebugStateType {
|
||||
|
||||
enum class QueueType {
|
||||
dcb = 0,
|
||||
ccb = 1,
|
||||
acb = 2,
|
||||
};
|
||||
|
||||
struct QueueDump {
|
||||
QueueType type;
|
||||
u32 submit_num;
|
||||
u32 num2; // acb: queue_num; else: buffer_in_submit
|
||||
std::vector<u32> data;
|
||||
uintptr_t base_addr;
|
||||
};
|
||||
|
||||
struct ShaderDump {
|
||||
Vulkan::Liverpool::ShaderProgram user_data{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
||||
struct ComputerShaderDump {
|
||||
Vulkan::Liverpool::ComputeProgram cs_program{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
||||
struct RegDump {
|
||||
bool is_compute{false};
|
||||
static constexpr size_t MaxShaderStages = 5;
|
||||
Vulkan::Liverpool::Regs regs{};
|
||||
std::array<ShaderDump, MaxShaderStages> stages{};
|
||||
ComputerShaderDump cs_data{};
|
||||
};
|
||||
|
||||
struct FrameDump {
|
||||
u32 frame_id;
|
||||
std::vector<QueueDump> queues;
|
||||
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
|
||||
};
|
||||
|
||||
class DebugStateImpl {
|
||||
friend class Core::Devtools::Layer;
|
||||
friend class Core::Devtools::Widget::FrameGraph;
|
||||
|
||||
std::mutex guest_threads_mutex{};
|
||||
std::vector<ThreadID> guest_threads{};
|
||||
std::atomic_bool is_guest_threads_paused = false;
|
||||
u64 pause_time{};
|
||||
|
||||
std::atomic_int32_t flip_frame_count = 0;
|
||||
std::atomic_int32_t gnm_frame_count = 0;
|
||||
|
||||
s32 gnm_frame_dump_request_count = -1;
|
||||
std::unordered_map<size_t, FrameDump*> waiting_reg_dumps;
|
||||
std::unordered_map<size_t, std::string> waiting_reg_dumps_dbg;
|
||||
bool waiting_submit_pause = false;
|
||||
bool should_show_frame_dump = false;
|
||||
|
||||
std::shared_mutex frame_dump_list_mutex;
|
||||
std::vector<FrameDump> frame_dump_list{};
|
||||
|
||||
std::queue<std::string> debug_message_popup;
|
||||
|
||||
public:
|
||||
void ShowDebugMessage(std::string message) {
|
||||
if (message.empty()) {
|
||||
return;
|
||||
}
|
||||
debug_message_popup.push(std::move(message));
|
||||
}
|
||||
|
||||
void AddCurrentThreadToGuestList();
|
||||
|
||||
void RemoveCurrentThreadFromGuestList();
|
||||
|
||||
void PauseGuestThreads();
|
||||
|
||||
void ResumeGuestThreads();
|
||||
|
||||
bool IsGuestThreadsPaused() const {
|
||||
return is_guest_threads_paused;
|
||||
}
|
||||
|
||||
void IncFlipFrameNum() {
|
||||
++flip_frame_count;
|
||||
}
|
||||
|
||||
void IncGnmFrameNum() {
|
||||
++gnm_frame_count;
|
||||
--gnm_frame_dump_request_count;
|
||||
}
|
||||
|
||||
u32 GetFrameNum() const {
|
||||
return flip_frame_count;
|
||||
}
|
||||
|
||||
bool DumpingCurrentFrame() const {
|
||||
return gnm_frame_dump_request_count > 0;
|
||||
}
|
||||
|
||||
bool DumpingCurrentReg() {
|
||||
std::shared_lock lock{frame_dump_list_mutex};
|
||||
return !waiting_reg_dumps.empty();
|
||||
}
|
||||
|
||||
bool ShouldPauseInSubmit() const {
|
||||
return waiting_submit_pause && gnm_frame_dump_request_count == 0;
|
||||
}
|
||||
|
||||
void RequestFrameDump(s32 count = 1);
|
||||
|
||||
FrameDump& GetFrameDump() {
|
||||
return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count];
|
||||
}
|
||||
|
||||
void PushQueueDump(QueueDump dump);
|
||||
|
||||
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
extern DebugStateType::DebugStateImpl& DebugState;
|
||||
297
src/core/devtools/gcn/gcn_context_regs.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#include "common/types.h"
|
||||
#include "gcn/si_ci_vi_merged_offset.h"
|
||||
|
||||
using namespace Pal::Gfx6;
|
||||
|
||||
namespace Core::Devtools::Gcn {
|
||||
const char* GetContextRegName(u32 reg_offset) {
|
||||
switch (reg_offset) {
|
||||
case mmDB_SHADER_CONTROL:
|
||||
return "mmDB_SHADER_CONTROL";
|
||||
case mmCB_SHADER_MASK:
|
||||
return "mmCB_SHADER_MASK";
|
||||
case mmPA_CL_CLIP_CNTL:
|
||||
return "mmPA_CL_CLIP_CNTL";
|
||||
case mmVGT_INSTANCE_STEP_RATE_0:
|
||||
return "mmVGT_INSTANCE_STEP_RATE_0";
|
||||
case mmVGT_INSTANCE_STEP_RATE_1:
|
||||
return "mmVGT_INSTANCE_STEP_RATE_1";
|
||||
case mmVGT_INDX_OFFSET:
|
||||
return "mmVGT_INDX_OFFSET";
|
||||
case mmVGT_SHADER_STAGES_EN:
|
||||
return "mmVGT_SHADER_STAGES_EN";
|
||||
case mmVGT_GS_MODE:
|
||||
return "mmVGT_GS_MODE";
|
||||
case mmVGT_STRMOUT_CONFIG:
|
||||
return "mmVGT_STRMOUT_CONFIG";
|
||||
case mmVGT_OUT_DEALLOC_CNTL:
|
||||
return "mmVGT_OUT_DEALLOC_CNTL";
|
||||
case mmVGT_VTX_CNT_EN:
|
||||
return "mmVGT_VTX_CNT_EN";
|
||||
case mmVGT_MAX_VTX_INDX:
|
||||
return "mmVGT_MAX_VTX_INDX";
|
||||
case mmVGT_MULTI_PRIM_IB_RESET_INDX:
|
||||
return "mmVGT_MULTI_PRIM_IB_RESET_INDX";
|
||||
case mmVGT_OUTPUT_PATH_CNTL:
|
||||
return "mmVGT_OUTPUT_PATH_CNTL";
|
||||
case mmVGT_GS_PER_ES:
|
||||
return "mmVGT_GS_PER_ES";
|
||||
case mmVGT_ES_PER_GS:
|
||||
return "mmVGT_ES_PER_GS";
|
||||
case mmVGT_GS_PER_VS:
|
||||
return "mmVGT_GS_PER_VS";
|
||||
case mmCB_COLOR0_BASE:
|
||||
return "mmCB_COLOR0_BASE";
|
||||
case mmCB_COLOR0_INFO:
|
||||
return "mmCB_COLOR0_INFO";
|
||||
case mmCB_COLOR0_CMASK_SLICE:
|
||||
return "mmCB_COLOR0_CMASK_SLICE";
|
||||
case mmCB_COLOR0_CLEAR_WORD0:
|
||||
return "mmCB_COLOR0_CLEAR_WORD0";
|
||||
case mmCB_COLOR0_CLEAR_WORD1:
|
||||
return "mmCB_COLOR0_CLEAR_WORD1";
|
||||
case mmCB_COLOR0_PITCH:
|
||||
return "mmCB_COLOR0_PITCH";
|
||||
case mmCB_COLOR0_SLICE:
|
||||
return "mmCB_COLOR0_SLICE";
|
||||
case mmCB_COLOR0_VIEW:
|
||||
return "mmCB_COLOR0_VIEW";
|
||||
case mmCB_COLOR0_DCC_CONTROL__VI:
|
||||
return "mmCB_COLOR0_DCC_CONTROL";
|
||||
case mmCB_COLOR0_CMASK:
|
||||
return "mmCB_COLOR0_CMASK";
|
||||
case mmCB_COLOR0_FMASK_SLICE:
|
||||
return "mmCB_COLOR0_FMASK_SLICE";
|
||||
case mmCB_COLOR0_FMASK:
|
||||
return "mmCB_COLOR0_FMASK";
|
||||
case mmCB_COLOR0_DCC_BASE__VI:
|
||||
return "mmCB_COLOR0_DCC_BASE";
|
||||
case mmCB_COLOR0_ATTRIB:
|
||||
return "mmCB_COLOR0_ATTRIB";
|
||||
case mmCB_COLOR1_BASE:
|
||||
return "mmCB_COLOR1_BASE";
|
||||
case mmCB_COLOR1_INFO:
|
||||
return "mmCB_COLOR1_INFO";
|
||||
case mmCB_COLOR1_ATTRIB:
|
||||
return "mmCB_COLOR1_ATTRIB";
|
||||
case mmCB_COLOR1_CMASK_SLICE:
|
||||
return "mmCB_COLOR1_CMASK_SLICE";
|
||||
case mmCB_COLOR1_CLEAR_WORD0:
|
||||
return "mmCB_COLOR1_CLEAR_WORD0";
|
||||
case mmCB_COLOR1_CLEAR_WORD1:
|
||||
return "mmCB_COLOR1_CLEAR_WORD1";
|
||||
case mmCB_COLOR1_PITCH:
|
||||
return "mmCB_COLOR1_PITCH";
|
||||
case mmCB_COLOR1_VIEW:
|
||||
return "mmCB_COLOR1_VIEW";
|
||||
case mmCB_COLOR2_INFO:
|
||||
return "mmCB_COLOR2_INFO";
|
||||
case mmCB_COLOR2_ATTRIB:
|
||||
return "mmCB_COLOR2_ATTRIB";
|
||||
case mmCB_COLOR2_CMASK_SLICE:
|
||||
return "mmCB_COLOR2_CMASK_SLICE";
|
||||
case mmCB_COLOR2_CLEAR_WORD0:
|
||||
return "mmCB_COLOR2_CLEAR_WORD0";
|
||||
case mmCB_COLOR2_CLEAR_WORD1:
|
||||
return "mmCB_COLOR2_CLEAR_WORD1";
|
||||
case mmCB_COLOR2_PITCH:
|
||||
return "mmCB_COLOR2_PITCH";
|
||||
case mmCB_COLOR2_VIEW:
|
||||
return "mmCB_COLOR2_VIEW";
|
||||
case mmCB_COLOR3_INFO:
|
||||
return "mmCB_COLOR3_INFO";
|
||||
case mmCB_COLOR3_CMASK_SLICE:
|
||||
return "mmCB_COLOR3_CMASK_SLICE";
|
||||
case mmCB_COLOR4_INFO:
|
||||
return "mmCB_COLOR4_INFO";
|
||||
case mmCB_COLOR5_INFO:
|
||||
return "mmCB_COLOR5_INFO";
|
||||
case mmCB_COLOR6_INFO:
|
||||
return "mmCB_COLOR6_INFO";
|
||||
case mmCB_COLOR7_INFO:
|
||||
return "mmCB_COLOR7_INFO";
|
||||
case mmDB_SRESULTS_COMPARE_STATE0:
|
||||
return "mmDB_SRESULTS_COMPARE_STATE0";
|
||||
case mmDB_SRESULTS_COMPARE_STATE1:
|
||||
return "mmDB_SRESULTS_COMPARE_STATE1";
|
||||
case mmDB_DEPTH_CONTROL:
|
||||
return "mmDB_DEPTH_CONTROL";
|
||||
case mmDB_EQAA:
|
||||
return "mmDB_EQAA";
|
||||
case mmPA_SU_POINT_SIZE:
|
||||
return "mmPA_SU_POINT_SIZE";
|
||||
case mmPA_SU_POINT_MINMAX:
|
||||
return "mmPA_SU_POINT_MINMAX";
|
||||
case mmPA_SU_SC_MODE_CNTL:
|
||||
return "mmPA_SU_SC_MODE_CNTL";
|
||||
case mmPA_SU_POLY_OFFSET_DB_FMT_CNTL:
|
||||
return "mmPA_SU_POLY_OFFSET_DB_FMT_CNTL";
|
||||
case mmPA_SC_CLIPRECT_RULE:
|
||||
return "mmPA_SC_CLIPRECT_RULE";
|
||||
case mmPA_SC_MODE_CNTL_0:
|
||||
return "mmPA_SC_MODE_CNTL_0";
|
||||
case mmPA_SC_MODE_CNTL_1:
|
||||
return "mmPA_SC_MODE_CNTL_1";
|
||||
case mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0:
|
||||
return "mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0";
|
||||
case mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0:
|
||||
return "mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0";
|
||||
case mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0:
|
||||
return "mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0";
|
||||
case mmPA_SC_AA_MASK_X0Y0_X1Y0:
|
||||
return "mmPA_SC_AA_MASK_X0Y0_X1Y0";
|
||||
case mmPA_SC_AA_MASK_X0Y1_X1Y1:
|
||||
return "mmPA_SC_AA_MASK_X0Y1_X1Y1";
|
||||
case mmPA_SC_CENTROID_PRIORITY_0:
|
||||
return "mmPA_SC_CENTROID_PRIORITY_0";
|
||||
case mmPA_SC_CENTROID_PRIORITY_1:
|
||||
return "mmPA_SC_CENTROID_PRIORITY_1";
|
||||
case mmPA_SC_AA_CONFIG:
|
||||
return "mmPA_SC_AA_CONFIG";
|
||||
case mmDB_RENDER_CONTROL:
|
||||
return "mmDB_RENDER_CONTROL";
|
||||
case mmDB_STENCIL_CONTROL:
|
||||
return "mmDB_STENCIL_CONTROL";
|
||||
case mmDB_STENCILREFMASK:
|
||||
return "mmDB_STENCILREFMASK";
|
||||
case mmDB_STENCILREFMASK_BF:
|
||||
return "mmDB_STENCILREFMASK_BF";
|
||||
case mmDB_STENCIL_CLEAR:
|
||||
return "mmDB_STENCIL_CLEAR";
|
||||
case mmDB_DEPTH_CLEAR:
|
||||
return "mmDB_DEPTH_CLEAR";
|
||||
case mmCB_TARGET_MASK:
|
||||
return "mmCB_TARGET_MASK";
|
||||
case mmDB_Z_INFO:
|
||||
return "mmDB_Z_INFO";
|
||||
case mmDB_STENCIL_INFO:
|
||||
return "mmDB_STENCIL_INFO";
|
||||
case mmDB_Z_READ_BASE:
|
||||
return "mmDB_Z_READ_BASE";
|
||||
case mmDB_STENCIL_READ_BASE:
|
||||
return "mmDB_STENCIL_READ_BASE";
|
||||
case mmDB_Z_WRITE_BASE:
|
||||
return "mmDB_Z_WRITE_BASE";
|
||||
case mmDB_STENCIL_WRITE_BASE:
|
||||
return "mmDB_STENCIL_WRITE_BASE";
|
||||
case mmDB_DEPTH_INFO:
|
||||
return "mmDB_DEPTH_INFO";
|
||||
case mmDB_DEPTH_VIEW:
|
||||
return "mmDB_DEPTH_VIEW";
|
||||
case mmDB_DEPTH_SLICE:
|
||||
return "mmDB_DEPTH_SLICE";
|
||||
case mmDB_DEPTH_SIZE:
|
||||
return "mmDB_DEPTH_SIZE";
|
||||
case mmTA_BC_BASE_ADDR:
|
||||
return "mmTA_BC_BASE_ADDR";
|
||||
case mmCB_BLEND_RED:
|
||||
return "mmCB_BLEND_RED";
|
||||
case mmCB_BLEND_GREEN:
|
||||
return "mmCB_BLEND_GREEN";
|
||||
case mmCB_BLEND_BLUE:
|
||||
return "mmCB_BLEND_BLUE";
|
||||
case mmDB_ALPHA_TO_MASK:
|
||||
return "mmDB_ALPHA_TO_MASK";
|
||||
case mmCB_BLEND0_CONTROL:
|
||||
return "mmCB_BLEND0_CONTROL";
|
||||
case mmCB_BLEND1_CONTROL:
|
||||
return "mmCB_BLEND1_CONTROL";
|
||||
case mmCB_BLEND2_CONTROL:
|
||||
return "mmCB_BLEND2_CONTROL";
|
||||
case mmCB_BLEND3_CONTROL:
|
||||
return "mmCB_BLEND3_CONTROL";
|
||||
case mmCB_BLEND4_CONTROL:
|
||||
return "mmCB_BLEND4_CONTROL";
|
||||
case mmCB_BLEND5_CONTROL:
|
||||
return "mmCB_BLEND5_CONTROL";
|
||||
case mmCB_BLEND6_CONTROL:
|
||||
return "mmCB_BLEND6_CONTROL";
|
||||
case mmCB_BLEND7_CONTROL:
|
||||
return "mmCB_BLEND7_CONTROL";
|
||||
case mmDB_HTILE_DATA_BASE:
|
||||
return "mmDB_HTILE_DATA_BASE";
|
||||
case mmDB_HTILE_SURFACE:
|
||||
return "mmDB_HTILE_SURFACE";
|
||||
case mmPA_SU_LINE_CNTL:
|
||||
return "mmPA_SU_LINE_CNTL";
|
||||
case mmPA_SC_VPORT_ZMIN_0:
|
||||
return "mmPA_SC_VPORT_ZMIN_0";
|
||||
case mmPA_SC_VPORT_ZMAX_0:
|
||||
return "mmPA_SC_VPORT_ZMAX_0";
|
||||
case mmPA_SC_VPORT_SCISSOR_0_TL:
|
||||
return "mmPA_SC_VPORT_SCISSOR_0_TL";
|
||||
case mmPA_SC_VPORT_SCISSOR_0_BR:
|
||||
return "mmPA_SC_VPORT_SCISSOR_0_BR";
|
||||
case mmPA_SC_GENERIC_SCISSOR_TL:
|
||||
return "mmPA_SC_GENERIC_SCISSOR_TL";
|
||||
case mmPA_SC_GENERIC_SCISSOR_BR:
|
||||
return "mmPA_SC_GENERIC_SCISSOR_BR";
|
||||
case mmPA_CL_VPORT_XSCALE:
|
||||
return "mmPA_CL_VPORT_XSCALE";
|
||||
case mmPA_CL_VPORT_YSCALE:
|
||||
return "mmPA_CL_VPORT_YSCALE";
|
||||
case mmPA_CL_VPORT_ZSCALE:
|
||||
return "mmPA_CL_VPORT_ZSCALE";
|
||||
case mmPA_CL_VPORT_XOFFSET:
|
||||
return "mmPA_CL_VPORT_XOFFSET";
|
||||
case mmPA_CL_VPORT_YOFFSET:
|
||||
return "mmPA_CL_VPORT_YOFFSET";
|
||||
case mmPA_CL_VPORT_ZOFFSET:
|
||||
return "mmPA_CL_VPORT_ZOFFSET";
|
||||
case mmPA_CL_VTE_CNTL:
|
||||
return "mmPA_CL_VTE_CNTL";
|
||||
case mmPA_SC_SCREEN_SCISSOR_TL:
|
||||
return "mmPA_SC_SCREEN_SCISSOR_TL";
|
||||
case mmPA_SC_SCREEN_SCISSOR_BR:
|
||||
return "mmPA_SC_SCREEN_SCISSOR_BR";
|
||||
case mmPA_SU_HARDWARE_SCREEN_OFFSET:
|
||||
return "mmPA_SU_HARDWARE_SCREEN_OFFSET";
|
||||
case mmPA_SU_VTX_CNTL:
|
||||
return "mmPA_SU_VTX_CNTL";
|
||||
case mmPA_CL_GB_VERT_CLIP_ADJ:
|
||||
return "mmPA_CL_GB_VERT_CLIP_ADJ";
|
||||
case mmPA_CL_GB_HORZ_CLIP_ADJ:
|
||||
return "mmPA_CL_GB_HORZ_CLIP_ADJ";
|
||||
case mmPA_CL_GB_VERT_DISC_ADJ:
|
||||
return "mmPA_CL_GB_VERT_DISC_ADJ";
|
||||
case mmPA_CL_GB_HORZ_DISC_ADJ:
|
||||
return "mmPA_CL_GB_HORZ_DISC_ADJ";
|
||||
case mmCB_COLOR_CONTROL:
|
||||
return "mmCB_COLOR_CONTROL";
|
||||
case mmSPI_SHADER_Z_FORMAT:
|
||||
return "mmSPI_SHADER_Z_FORMAT";
|
||||
case mmSPI_SHADER_COL_FORMAT:
|
||||
return "mmSPI_SHADER_COL_FORMAT";
|
||||
case mmPA_CL_VS_OUT_CNTL:
|
||||
return "mmPA_CL_VS_OUT_CNTL";
|
||||
case mmSPI_VS_OUT_CONFIG:
|
||||
return "mmSPI_VS_OUT_CONFIG";
|
||||
case mmSPI_SHADER_POS_FORMAT:
|
||||
return "mmSPI_SHADER_POS_FORMAT";
|
||||
case mmSPI_PS_INPUT_ENA:
|
||||
return "mmSPI_PS_INPUT_ENA";
|
||||
case mmSPI_PS_INPUT_ADDR:
|
||||
return "mmSPI_PS_INPUT_ADDR";
|
||||
case mmSPI_PS_IN_CONTROL:
|
||||
return "mmSPI_PS_IN_CONTROL";
|
||||
case mmSPI_BARYC_CNTL:
|
||||
return "mmSPI_BARYC_CNTL";
|
||||
case mmSPI_PS_INPUT_CNTL_0:
|
||||
return "mmSPI_PS_INPUT_CNTL_0";
|
||||
case mmSPI_PS_INPUT_CNTL_1:
|
||||
return "mmSPI_PS_INPUT_CNTL_1";
|
||||
case mmSPI_PS_INPUT_CNTL_2:
|
||||
return "mmSPI_PS_INPUT_CNTL_2";
|
||||
case mmSPI_PS_INPUT_CNTL_3:
|
||||
return "mmSPI_PS_INPUT_CNTL_3";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<UNK>";
|
||||
}
|
||||
} // namespace Core::Devtools::Gcn
|
||||
118
src/core/devtools/gcn/gcn_op_names.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#include "common/types.h"
|
||||
#include "gcn/si_ci_vi_merged_pm4_it_opcodes.h"
|
||||
|
||||
namespace Core::Devtools::Gcn {
|
||||
const char* GetOpCodeName(u32 op) {
|
||||
switch (op) {
|
||||
case IT_NOP:
|
||||
return "IT_NOP";
|
||||
case IT_SET_BASE:
|
||||
return "IT_SET_BASE";
|
||||
case IT_INDEX_BUFFER_SIZE:
|
||||
return "IT_INDEX_BUFFER_SIZE";
|
||||
case IT_SET_PREDICATION:
|
||||
return "IT_SET_PREDICATION";
|
||||
case IT_COND_EXEC:
|
||||
return "IT_COND_EXEC";
|
||||
case IT_INDEX_BASE:
|
||||
return "IT_INDEX_BASE";
|
||||
case IT_INDEX_TYPE:
|
||||
return "IT_INDEX_TYPE";
|
||||
case IT_NUM_INSTANCES:
|
||||
return "IT_NUM_INSTANCES";
|
||||
case IT_STRMOUT_BUFFER_UPDATE:
|
||||
return "IT_STRMOUT_BUFFER_UPDATE";
|
||||
case IT_WRITE_DATA:
|
||||
return "IT_WRITE_DATA";
|
||||
case IT_MEM_SEMAPHORE:
|
||||
return "IT_MEM_SEMAPHORE";
|
||||
case IT_WAIT_REG_MEM:
|
||||
return "IT_WAIT_REG_MEM";
|
||||
case IT_INDIRECT_BUFFER:
|
||||
return "IT_INDIRECT_BUFFER";
|
||||
case IT_PFP_SYNC_ME:
|
||||
return "IT_PFP_SYNC_ME";
|
||||
case IT_EVENT_WRITE:
|
||||
return "IT_EVENT_WRITE";
|
||||
case IT_EVENT_WRITE_EOP:
|
||||
return "IT_EVENT_WRITE_EOP";
|
||||
case IT_EVENT_WRITE_EOS:
|
||||
return "IT_EVENT_WRITE_EOS";
|
||||
case IT_DMA_DATA__CI__VI:
|
||||
return "IT_DMA_DATA";
|
||||
case IT_ACQUIRE_MEM__CI__VI:
|
||||
return "IT_ACQUIRE_MEM";
|
||||
case IT_REWIND__CI__VI:
|
||||
return "IT_REWIND";
|
||||
case IT_SET_CONFIG_REG:
|
||||
return "IT_SET_CONFIG_REG";
|
||||
case IT_SET_CONTEXT_REG:
|
||||
return "IT_SET_CONTEXT_REG";
|
||||
case IT_SET_SH_REG:
|
||||
return "IT_SET_SH_REG";
|
||||
case IT_SET_UCONFIG_REG__CI__VI:
|
||||
return "IT_SET_UCONFIG_REG";
|
||||
case IT_INCREMENT_DE_COUNTER:
|
||||
return "IT_INCREMENT_DE_COUNTER";
|
||||
case IT_WAIT_ON_CE_COUNTER:
|
||||
return "IT_WAIT_ON_CE_COUNTER";
|
||||
case IT_DISPATCH_DIRECT:
|
||||
return "IT_DISPATCH_DIRECT";
|
||||
case IT_DISPATCH_INDIRECT:
|
||||
return "IT_DISPATCH_INDIRECT";
|
||||
case IT_OCCLUSION_QUERY:
|
||||
return "IT_OCCLUSION_QUERY";
|
||||
case IT_REG_RMW:
|
||||
return "IT_REG_RMW";
|
||||
case IT_PRED_EXEC:
|
||||
return "IT_PRED_EXEC";
|
||||
case IT_DRAW_INDIRECT:
|
||||
return "IT_DRAW_INDIRECT";
|
||||
case IT_DRAW_INDEX_INDIRECT:
|
||||
return "IT_DRAW_INDEX_INDIRECT";
|
||||
case IT_DRAW_INDEX_2:
|
||||
return "IT_DRAW_INDEX_2";
|
||||
case IT_DRAW_INDEX_OFFSET_2:
|
||||
return "IT_DRAW_INDEX_OFFSET_2";
|
||||
case IT_CONTEXT_CONTROL:
|
||||
return "IT_CONTEXT_CONTROL";
|
||||
case IT_DRAW_INDIRECT_MULTI:
|
||||
return "IT_DRAW_INDIRECT_MULTI";
|
||||
case IT_DRAW_INDEX_AUTO:
|
||||
return "IT_DRAW_INDEX_AUTO";
|
||||
case IT_DRAW_INDEX_MULTI_AUTO:
|
||||
return "IT_DRAW_INDEX_MULTI_AUTO";
|
||||
case IT_COPY_DATA:
|
||||
return "IT_COPY_DATA";
|
||||
case IT_CP_DMA:
|
||||
return "IT_CP_DMA";
|
||||
case IT_SURFACE_SYNC:
|
||||
return "IT_SURFACE_SYNC";
|
||||
case IT_COND_WRITE:
|
||||
return "IT_COND_WRITE";
|
||||
case IT_RELEASE_MEM__CI__VI:
|
||||
return "IT_RELEASE_MEM";
|
||||
case IT_WRITE_CONST_RAM:
|
||||
return "IT_WRITE_CONST_RAM"; // used in CCB
|
||||
case IT_WAIT_ON_DE_COUNTER_DIFF:
|
||||
return "IT_WAIT_ON_DE_COUNTER_DIFF"; // used in CCB
|
||||
case IT_DUMP_CONST_RAM:
|
||||
return "IT_DUMP_CONST_RAM"; // used in CCB
|
||||
case IT_INCREMENT_CE_COUNTER:
|
||||
return "IT_INCREMENT_CE_COUNTER"; // used in CCB
|
||||
case IT_CLEAR_STATE:
|
||||
return "IT_CLEAR_STATE";
|
||||
case 0xFF:
|
||||
return "<STUB (TMP)>";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "<UNK>";
|
||||
}
|
||||
} // namespace Core::Devtools::Gcn
|
||||
171
src/core/devtools/gcn/gcn_shader_regs.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#include "common/types.h"
|
||||
#include "gcn/si_ci_vi_merged_offset.h"
|
||||
|
||||
using namespace Pal::Gfx6;
|
||||
|
||||
namespace Core::Devtools::Gcn {
|
||||
const char* GetShaderRegName(u32 reg_offset) {
|
||||
switch (reg_offset) {
|
||||
case mmSPI_SHADER_PGM_LO_VS:
|
||||
return "mmSPI_SHADER_PGM_LO_VS";
|
||||
case mmSPI_SHADER_PGM_HI_VS:
|
||||
return "mmSPI_SHADER_PGM_HI_VS";
|
||||
case mmSPI_SHADER_PGM_LO_PS:
|
||||
return "mmSPI_SHADER_PGM_LO_PS";
|
||||
case mmSPI_SHADER_PGM_HI_PS:
|
||||
return "mmSPI_SHADER_PGM_HI_PS";
|
||||
case mmSPI_SHADER_PGM_RSRC1_VS:
|
||||
return "mmSPI_SHADER_PGM_RSRC1_VS";
|
||||
case mmSPI_SHADER_PGM_RSRC2_VS:
|
||||
return "mmSPI_SHADER_PGM_RSRC2_VS";
|
||||
case mmSPI_SHADER_PGM_RSRC3_VS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_VS__CI__VI";
|
||||
case mmSPI_SHADER_PGM_RSRC1_PS:
|
||||
return "mmSPI_SHADER_PGM_RSRC1_PS";
|
||||
case mmSPI_SHADER_PGM_RSRC2_PS:
|
||||
return "mmSPI_SHADER_PGM_RSRC2_PS";
|
||||
case mmSPI_SHADER_PGM_RSRC3_PS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_PS__CI__VI";
|
||||
case mmSPI_SHADER_USER_DATA_PS_0:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_0";
|
||||
case mmSPI_SHADER_USER_DATA_PS_1:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_1";
|
||||
case mmSPI_SHADER_USER_DATA_PS_2:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_2";
|
||||
case mmSPI_SHADER_USER_DATA_PS_3:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_3";
|
||||
case mmSPI_SHADER_USER_DATA_PS_4:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_4";
|
||||
case mmSPI_SHADER_USER_DATA_PS_5:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_5";
|
||||
case mmSPI_SHADER_USER_DATA_PS_6:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_6";
|
||||
case mmSPI_SHADER_USER_DATA_PS_7:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_7";
|
||||
case mmSPI_SHADER_USER_DATA_PS_8:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_8";
|
||||
case mmSPI_SHADER_USER_DATA_PS_9:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_9";
|
||||
case mmSPI_SHADER_USER_DATA_PS_10:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_10";
|
||||
case mmSPI_SHADER_USER_DATA_PS_11:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_11";
|
||||
case mmSPI_SHADER_USER_DATA_PS_12:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_12";
|
||||
case mmSPI_SHADER_USER_DATA_PS_13:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_13";
|
||||
case mmSPI_SHADER_USER_DATA_PS_14:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_14";
|
||||
case mmSPI_SHADER_USER_DATA_PS_15:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_15";
|
||||
case mmCOMPUTE_TMPRING_SIZE:
|
||||
return "mmCOMPUTE_TMPRING_SIZE";
|
||||
case mmCOMPUTE_PGM_LO:
|
||||
return "mmCOMPUTE_PGM_LO";
|
||||
case mmCOMPUTE_PGM_HI:
|
||||
return "mmCOMPUTE_PGM_HI";
|
||||
case mmCOMPUTE_PGM_RSRC1:
|
||||
return "mmCOMPUTE_PGM_RSRC1";
|
||||
case mmCOMPUTE_PGM_RSRC2:
|
||||
return "mmCOMPUTE_PGM_RSRC2";
|
||||
case mmCOMPUTE_USER_DATA_0:
|
||||
return "mmCOMPUTE_USER_DATA_0";
|
||||
case mmCOMPUTE_USER_DATA_1:
|
||||
return "mmCOMPUTE_USER_DATA_1";
|
||||
case mmCOMPUTE_USER_DATA_2:
|
||||
return "mmCOMPUTE_USER_DATA_2";
|
||||
case mmCOMPUTE_USER_DATA_3:
|
||||
return "mmCOMPUTE_USER_DATA_3";
|
||||
case mmCOMPUTE_USER_DATA_4:
|
||||
return "mmCOMPUTE_USER_DATA_4";
|
||||
case mmCOMPUTE_USER_DATA_5:
|
||||
return "mmCOMPUTE_USER_DATA_5";
|
||||
case mmCOMPUTE_USER_DATA_6:
|
||||
return "mmCOMPUTE_USER_DATA_6";
|
||||
case mmCOMPUTE_USER_DATA_7:
|
||||
return "mmCOMPUTE_USER_DATA_7";
|
||||
case mmCOMPUTE_USER_DATA_8:
|
||||
return "mmCOMPUTE_USER_DATA_8";
|
||||
case mmCOMPUTE_USER_DATA_9:
|
||||
return "mmCOMPUTE_USER_DATA_9";
|
||||
case mmCOMPUTE_USER_DATA_10:
|
||||
return "mmCOMPUTE_USER_DATA_10";
|
||||
case mmCOMPUTE_USER_DATA_11:
|
||||
return "mmCOMPUTE_USER_DATA_11";
|
||||
case mmCOMPUTE_USER_DATA_12:
|
||||
return "mmCOMPUTE_USER_DATA_12";
|
||||
case mmCOMPUTE_USER_DATA_13:
|
||||
return "mmCOMPUTE_USER_DATA_13";
|
||||
case mmCOMPUTE_USER_DATA_14:
|
||||
return "mmCOMPUTE_USER_DATA_14";
|
||||
case mmCOMPUTE_USER_DATA_15:
|
||||
return "mmCOMPUTE_USER_DATA_15";
|
||||
case mmCOMPUTE_NUM_THREAD_X:
|
||||
return "mmCOMPUTE_NUM_THREAD_X";
|
||||
case mmCOMPUTE_NUM_THREAD_Y:
|
||||
return "mmCOMPUTE_NUM_THREAD_Y";
|
||||
case mmCOMPUTE_NUM_THREAD_Z:
|
||||
return "mmCOMPUTE_NUM_THREAD_Z";
|
||||
case mmCOMPUTE_STATIC_THREAD_MGMT_SE0:
|
||||
return "mmCOMPUTE_STATIC_THREAD_MGMT_SE0";
|
||||
case mmCOMPUTE_STATIC_THREAD_MGMT_SE1:
|
||||
return "mmCOMPUTE_STATIC_THREAD_MGMT_SE1";
|
||||
case mmCOMPUTE_RESOURCE_LIMITS:
|
||||
return "mmCOMPUTE_RESOURCE_LIMITS";
|
||||
case mmSPI_SHADER_USER_DATA_VS_0:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_0";
|
||||
case mmSPI_SHADER_USER_DATA_VS_1:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_1";
|
||||
case mmSPI_SHADER_USER_DATA_VS_2:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_2";
|
||||
case mmSPI_SHADER_USER_DATA_VS_3:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_3";
|
||||
case mmSPI_SHADER_USER_DATA_VS_4:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_4";
|
||||
case mmSPI_SHADER_USER_DATA_VS_5:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_5";
|
||||
case mmSPI_SHADER_USER_DATA_VS_6:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_6";
|
||||
case mmSPI_SHADER_USER_DATA_VS_7:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_7";
|
||||
case mmSPI_SHADER_USER_DATA_VS_8:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_8";
|
||||
case mmSPI_SHADER_USER_DATA_VS_9:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_9";
|
||||
case mmSPI_SHADER_USER_DATA_VS_10:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_10";
|
||||
case mmSPI_SHADER_USER_DATA_VS_11:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_11";
|
||||
case mmSPI_SHADER_USER_DATA_VS_12:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_12";
|
||||
case mmSPI_SHADER_USER_DATA_VS_13:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_13";
|
||||
case mmSPI_SHADER_USER_DATA_VS_14:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_14";
|
||||
case mmSPI_SHADER_USER_DATA_VS_15:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_15";
|
||||
case mmSPI_SHADER_USER_DATA_HS_0:
|
||||
return "mmSPI_SHADER_USER_DATA_HS_0";
|
||||
case mmSPI_SHADER_USER_DATA_HS_1:
|
||||
return "mmSPI_SHADER_USER_DATA_HS_1";
|
||||
case mmSPI_SHADER_USER_DATA_HS_9:
|
||||
return "mmSPI_SHADER_USER_DATA_HS_9";
|
||||
case mmSPI_SHADER_PGM_RSRC3_GS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_GS__CI__VI";
|
||||
case mmSPI_SHADER_PGM_RSRC3_ES__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_ES__CI__VI";
|
||||
case mmSPI_SHADER_PGM_RSRC3_LS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_LS__CI__VI";
|
||||
case mmSPI_SHADER_LATE_ALLOC_VS__CI__VI:
|
||||
return "mmSPI_SHADER_LATE_ALLOC_VS__CI__VI";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<UNK>";
|
||||
}
|
||||
} // namespace Core::Devtools::Gcn
|
||||
8
src/core/devtools/help.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
R"(
|
||||
* If you hold shift, you can move the window without docking it.
|
||||
* You don't need to close every window you open. When a parent window is closed, all its children will be closed too.
|
||||
* If you want to inspect or compare more than 1 frame dump without undocking, there's a option to keep showing opened popups even when in hide/minimize the frame dump window.
|
||||
* To use the disassembly viewer, you need to set up a cli to use a external disassembler and use "{src}" as a placeholder for the source code file, e.g. dis.exe --some-opt "{src}"
|
||||
)"
|
||||
346
src/core/devtools/layer.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "layer.h"
|
||||
#include "options.h"
|
||||
#include "widget/frame_dump.h"
|
||||
#include "widget/frame_graph.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace Core::Devtools;
|
||||
using L = Core::Devtools::Layer;
|
||||
|
||||
static bool show_simple_fps = false;
|
||||
static bool visibility_toggled = false;
|
||||
|
||||
static float fps_scale = 1.0f;
|
||||
static bool show_advanced_debug = false;
|
||||
static int dump_frame_count = 1;
|
||||
|
||||
static Widget::FrameGraph frame_graph;
|
||||
static std::vector<Widget::FrameDumpViewer> frame_viewers;
|
||||
|
||||
static float debug_popup_timing = 3.0f;
|
||||
|
||||
static bool just_opened_options = false;
|
||||
|
||||
// clang-format off
|
||||
static std::string help_text =
|
||||
#include "help.txt"
|
||||
;
|
||||
// clang-format on
|
||||
|
||||
void L::DrawMenuBar() {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
bool open_popup_options = false;
|
||||
bool open_popup_help = false;
|
||||
|
||||
if (BeginMainMenuBar()) {
|
||||
if (BeginMenu("Options")) {
|
||||
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
||||
if (isSystemPaused) {
|
||||
DebugState.ResumeGuestThreads();
|
||||
} else {
|
||||
DebugState.PauseGuestThreads();
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (BeginMenu("GPU Tools")) {
|
||||
MenuItem("Show frame info", nullptr, &frame_graph.is_open);
|
||||
if (BeginMenu("Dump frames")) {
|
||||
SliderInt("Count", &dump_frame_count, 1, 5);
|
||||
if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) {
|
||||
DebugState.RequestFrameDump(dump_frame_count);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
open_popup_options = MenuItem("Options");
|
||||
open_popup_help = MenuItem("Help & Tips");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMainMenuBar();
|
||||
}
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_F9, false)) {
|
||||
if (io.KeyCtrl && io.KeyAlt) {
|
||||
if (!DebugState.ShouldPauseInSubmit()) {
|
||||
DebugState.RequestFrameDump(dump_frame_count);
|
||||
}
|
||||
}
|
||||
if (!io.KeyCtrl && !io.KeyAlt) {
|
||||
if (isSystemPaused) {
|
||||
DebugState.ResumeGuestThreads();
|
||||
} else {
|
||||
DebugState.PauseGuestThreads();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (open_popup_options) {
|
||||
OpenPopup("GPU Tools Options");
|
||||
just_opened_options = true;
|
||||
}
|
||||
if (open_popup_help) {
|
||||
OpenPopup("HelpTips");
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawAdvanced() {
|
||||
DrawMenuBar();
|
||||
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
frame_graph.Draw();
|
||||
|
||||
if (isSystemPaused) {
|
||||
GetForegroundDrawList(GetMainViewport())
|
||||
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
||||
}
|
||||
|
||||
if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) {
|
||||
DebugState.should_show_frame_dump = false;
|
||||
std::unique_lock lock{DebugState.frame_dump_list_mutex};
|
||||
while (!DebugState.frame_dump_list.empty()) {
|
||||
const auto& frame_dump = DebugState.frame_dump_list.back();
|
||||
frame_viewers.emplace_back(frame_dump);
|
||||
DebugState.frame_dump_list.pop_back();
|
||||
}
|
||||
static bool first_time = true;
|
||||
if (first_time) {
|
||||
first_time = false;
|
||||
DebugState.ShowDebugMessage("Tip: You can shift+click any\n"
|
||||
"popup to open a new window");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = frame_viewers.begin(); it != frame_viewers.end();) {
|
||||
if (it->is_open) {
|
||||
it->Draw();
|
||||
++it;
|
||||
} else {
|
||||
it = frame_viewers.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
if (!DebugState.debug_message_popup.empty()) {
|
||||
if (debug_popup_timing > 0.0f) {
|
||||
debug_popup_timing -= io.DeltaTime;
|
||||
if (Begin("##devtools_msg", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove)) {
|
||||
BringWindowToDisplayFront(GetCurrentWindow());
|
||||
const auto display_size = io.DisplaySize;
|
||||
const auto& msg = DebugState.debug_message_popup.front();
|
||||
const auto padding = GetStyle().WindowPadding;
|
||||
const auto txt_size = CalcTextSize(&msg.front(), &msg.back() + 1, false, 250.0f);
|
||||
SetWindowPos({display_size.x - padding.x * 2.0f - txt_size.x, 50.0f});
|
||||
SetWindowSize({txt_size.x + padding.x * 2.0f, txt_size.y + padding.y * 2.0f});
|
||||
PushTextWrapPos(250.0f);
|
||||
TextEx(&msg.front(), &msg.back() + 1);
|
||||
PopTextWrapPos();
|
||||
}
|
||||
End();
|
||||
} else {
|
||||
DebugState.debug_message_popup.pop();
|
||||
debug_popup_timing = 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool close_popup_options = true;
|
||||
if (BeginPopupModal("GPU Tools Options", &close_popup_options,
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
static char disassembly_cli[512];
|
||||
static bool frame_dump_render_on_collapse;
|
||||
|
||||
if (just_opened_options) {
|
||||
just_opened_options = false;
|
||||
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
|
||||
disassembly_cli[s] = '\0';
|
||||
frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse;
|
||||
}
|
||||
|
||||
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
|
||||
}
|
||||
Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse);
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip("When a frame dump is collapsed, it will keep\n"
|
||||
"showing all opened popups related to it");
|
||||
}
|
||||
|
||||
if (Button("Save")) {
|
||||
Options.disassembly_cli = disassembly_cli;
|
||||
Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse;
|
||||
SaveIniSettingsToDisk(io.IniFilename);
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
if (BeginPopup("HelpTips", ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove)) {
|
||||
CentralizeWindow();
|
||||
|
||||
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10.0f});
|
||||
PushTextWrapPos(600.0f);
|
||||
|
||||
const char* begin = help_text.data();
|
||||
TextUnformatted(begin, begin + help_text.size());
|
||||
|
||||
PopTextWrapPos();
|
||||
PopStyleVar();
|
||||
|
||||
EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawSimple() {
|
||||
const auto io = GetIO();
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
}
|
||||
|
||||
static void LoadSettings(const char* line) {
|
||||
int i;
|
||||
float f;
|
||||
if (sscanf(line, "fps_scale=%f", &f) == 1) {
|
||||
fps_scale = f;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_advanced_debug=%d", &i) == 1) {
|
||||
show_advanced_debug = i != 0;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_frame_graph=%d", &i) == 1) {
|
||||
frame_graph.is_open = i != 0;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "dump_frame_count=%d", &i) == 1) {
|
||||
dump_frame_count = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void L::SetupSettings() {
|
||||
frame_graph.is_open = true;
|
||||
|
||||
using SettingLoader = void (*)(const char*);
|
||||
|
||||
ImGuiSettingsHandler handler{};
|
||||
handler.TypeName = "DevtoolsLayer";
|
||||
handler.TypeHash = ImHashStr(handler.TypeName);
|
||||
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
|
||||
if (std::string_view("Data") == name) {
|
||||
static_assert(std::is_same_v<decltype(&LoadSettings), SettingLoader>);
|
||||
return (void*)&LoadSettings;
|
||||
}
|
||||
if (std::string_view("CmdList") == name) {
|
||||
static_assert(
|
||||
std::is_same_v<decltype(&Widget::CmdListViewer::LoadConfig), SettingLoader>);
|
||||
return (void*)&Widget::CmdListViewer::LoadConfig;
|
||||
}
|
||||
if (std::string_view("Options") == name) {
|
||||
static_assert(std::is_same_v<decltype(&LoadOptionsConfig), SettingLoader>);
|
||||
return (void*)&LoadOptionsConfig;
|
||||
}
|
||||
return (void*)nullptr;
|
||||
};
|
||||
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* handle, const char* line) {
|
||||
if (handle != nullptr) {
|
||||
reinterpret_cast<SettingLoader>(handle)(line);
|
||||
}
|
||||
};
|
||||
handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
|
||||
buf->appendf("[%s][Data]\n", handler->TypeName);
|
||||
buf->appendf("fps_scale=%f\n", fps_scale);
|
||||
buf->appendf("show_advanced_debug=%d\n", show_advanced_debug);
|
||||
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
||||
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
||||
buf->append("\n");
|
||||
buf->appendf("[%s][CmdList]\n", handler->TypeName);
|
||||
Widget::CmdListViewer::SerializeConfig(buf);
|
||||
buf->append("\n");
|
||||
buf->appendf("[%s][Options]\n", handler->TypeName);
|
||||
SerializeOptionsConfig(buf);
|
||||
buf->append("\n");
|
||||
};
|
||||
AddSettingsHandler(&handler);
|
||||
|
||||
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
DockBuilderAddNode(dock_id, 0);
|
||||
DockBuilderSetNodePos(dock_id, ImVec2{450.0, 150.0});
|
||||
DockBuilderSetNodeSize(dock_id, ImVec2{400.0, 500.0});
|
||||
DockBuilderFinish(dock_id);
|
||||
}
|
||||
|
||||
void L::Draw() {
|
||||
const auto io = GetIO();
|
||||
PushID("DevtoolsLayer");
|
||||
|
||||
if (!DebugState.IsGuestThreadsPaused()) {
|
||||
const auto fn = DebugState.flip_frame_count.load();
|
||||
frame_graph.AddFrame(fn, io.DeltaTime);
|
||||
}
|
||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||
if (io.KeyCtrl) {
|
||||
show_advanced_debug = !show_advanced_debug;
|
||||
} else {
|
||||
show_simple_fps = !show_simple_fps;
|
||||
}
|
||||
visibility_toggled = true;
|
||||
}
|
||||
|
||||
if (show_simple_fps) {
|
||||
if (Begin("Video Info", nullptr,
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
||||
// Set window position to top left if it was toggled on
|
||||
if (visibility_toggled) {
|
||||
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_Always);
|
||||
visibility_toggled = false;
|
||||
}
|
||||
if (BeginPopupContextWindow()) {
|
||||
#define M(label, value) \
|
||||
if (MenuItem(label, nullptr, fps_scale == value)) \
|
||||
fps_scale = value
|
||||
M("0.5x", 0.5f);
|
||||
M("1.0x", 1.0f);
|
||||
M("1.5x", 1.5f);
|
||||
M("2.0x", 2.0f);
|
||||
M("2.5x", 2.5f);
|
||||
EndPopup();
|
||||
#undef M
|
||||
}
|
||||
KeepWindowInside();
|
||||
SetWindowFontScale(fps_scale);
|
||||
DrawSimple();
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_advanced_debug) {
|
||||
PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]);
|
||||
PushID("DevtoolsLayer");
|
||||
DrawAdvanced();
|
||||
PopID();
|
||||
PopFont();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
24
src/core/devtools/layer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Core::Devtools {
|
||||
|
||||
class Layer final : public ImGui::Layer {
|
||||
|
||||
static void DrawMenuBar();
|
||||
|
||||
static void DrawAdvanced();
|
||||
|
||||
static void DrawSimple();
|
||||
|
||||
public:
|
||||
static void SetupSettings();
|
||||
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools
|
||||
30
src/core/devtools/options.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "options.h"
|
||||
|
||||
namespace Core::Devtools {
|
||||
|
||||
TOptions Options;
|
||||
|
||||
void LoadOptionsConfig(const char* line) {
|
||||
char str[512];
|
||||
int i;
|
||||
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) {
|
||||
Options.disassembly_cli = str;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) {
|
||||
Options.frame_dump_render_on_collapse = i != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
|
||||
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str());
|
||||
buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse);
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools
|
||||
22
src/core/devtools/options.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct ImGuiTextBuffer;
|
||||
|
||||
namespace Core::Devtools {
|
||||
|
||||
struct TOptions {
|
||||
std::string disassembly_cli{};
|
||||
bool frame_dump_render_on_collapse{false};
|
||||
};
|
||||
|
||||
extern TOptions Options;
|
||||
|
||||
void LoadOptionsConfig(const char* line);
|
||||
void SerializeOptionsConfig(ImGuiTextBuffer* buf);
|
||||
|
||||
} // namespace Core::Devtools
|
||||
1482
src/core/devtools/widget/cmd_list.cpp
Normal file
76
src/core/devtools/widget/cmd_list.h
Normal file
@@ -0,0 +1,76 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/types.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "reg_view.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
union PM4Type3Header;
|
||||
enum class PM4ItOpcode : u32;
|
||||
} // namespace AmdGpu
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class FrameDumpViewer;
|
||||
|
||||
void ParsePolygonControl(u32 value, bool begin_table = true);
|
||||
void ParseAaConfig(u32 value, bool begin_table = true);
|
||||
void ParseViewportControl(u32 value, bool begin_table = true);
|
||||
void ParseColorControl(u32 value, bool begin_table = true);
|
||||
void ParseColor0Info(u32 value, bool begin_table = true);
|
||||
void ParseColor0Attrib(u32 value, bool begin_table = true);
|
||||
void ParseBlendControl(u32 value, bool begin_table = true);
|
||||
void ParseDepthRenderControl(u32 value, bool begin_table = true);
|
||||
void ParseDepthControl(u32 value, bool begin_table = true);
|
||||
void ParseEqaa(u32 value, bool begin_table = true);
|
||||
void ParseZInfo(u32 value, bool begin_table = true);
|
||||
|
||||
class CmdListViewer {
|
||||
|
||||
DebugStateType::FrameDump* frame_dump;
|
||||
|
||||
uintptr_t base_addr;
|
||||
std::string name;
|
||||
std::vector<GPUEvent> events{};
|
||||
uintptr_t cmdb_addr;
|
||||
size_t cmdb_size;
|
||||
|
||||
std::string cmdb_view_name;
|
||||
MemoryEditor cmdb_view;
|
||||
|
||||
int batch_bp{-1};
|
||||
int vqid{255};
|
||||
u32 highlight_batch{~0u};
|
||||
|
||||
RegView batch_view;
|
||||
int last_selected_batch{-1};
|
||||
|
||||
std::vector<RegView> extra_batch_view;
|
||||
|
||||
static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
|
||||
public:
|
||||
static void LoadConfig(const char* line);
|
||||
static void SerializeConfig(ImGuiTextBuffer* buf);
|
||||
|
||||
explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
|
||||
uintptr_t base_addr = 0, std::string name = "");
|
||||
|
||||
void Draw(bool only_batches_view = false);
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
109
src/core/devtools/widget/common.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/pm4_opcodes.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
/*
|
||||
* Generic PM4 header
|
||||
*/
|
||||
union PM4Header {
|
||||
struct {
|
||||
u32 reserved : 16;
|
||||
u32 count : 14;
|
||||
u32 type : 2; // PM4_TYPE
|
||||
};
|
||||
u32 u32All;
|
||||
};
|
||||
|
||||
struct PushMarker {
|
||||
std::string name{};
|
||||
};
|
||||
|
||||
struct PopMarker {};
|
||||
|
||||
struct BatchBegin {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct BatchInfo {
|
||||
u32 id;
|
||||
std::string marker{};
|
||||
size_t start_addr;
|
||||
size_t end_addr;
|
||||
size_t command_addr;
|
||||
AmdGpu::PM4ItOpcode type;
|
||||
bool bypass{false};
|
||||
};
|
||||
|
||||
using GPUEvent = std::variant<PushMarker, PopMarker, BatchBegin, BatchInfo>;
|
||||
|
||||
template <typename... Args>
|
||||
void DrawRow(const char* text, const char* fmt, Args... args) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(text);
|
||||
ImGui::TableNextColumn();
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), fmt, args...);
|
||||
ImGui::TextUnformatted(buf);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DrawValueRow(const char* text, T value) {
|
||||
if constexpr (std::is_enum_v<T>) {
|
||||
return DrawRow(text, "%X (%s)", value, magic_enum::enum_name(value).data());
|
||||
} else if constexpr (std::is_integral_v<T>) {
|
||||
return DrawRow(text, "%X", value);
|
||||
} else if constexpr (std::is_base_of_v<BitField<T::position, T::bits, typename T::Type>, T>) {
|
||||
return DrawValueRow(text, value.Value());
|
||||
} else {
|
||||
static_assert(false, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V, typename... Extra>
|
||||
void DrawValueRowList(const char* text, V arg, Extra&&... extra_args) {
|
||||
DrawValueRow(text, arg);
|
||||
if constexpr (sizeof...(extra_args) > 0) {
|
||||
DrawValueRowList(std::forward<Extra>(extra_args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static void DoTooltip(const char* str_id, Args&&... args) {
|
||||
if (ImGui::BeginTooltip()) {
|
||||
if (ImGui::BeginTable(str_id, 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
DrawMultipleRow(std::forward<Args>(args)...);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
|
||||
using AmdGpu::PM4ItOpcode;
|
||||
switch (opcode) {
|
||||
case PM4ItOpcode::DrawIndex2:
|
||||
case PM4ItOpcode::DrawIndexOffset2:
|
||||
case PM4ItOpcode::DrawIndexAuto:
|
||||
case PM4ItOpcode::DrawIndirect:
|
||||
case PM4ItOpcode::DrawIndexIndirect:
|
||||
case PM4ItOpcode::DispatchDirect:
|
||||
case PM4ItOpcode::DispatchIndirect:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
197
src/core/devtools/widget/frame_dump.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdio>
|
||||
#include <fmt/chrono.h>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/devtools/options.h"
|
||||
#include "frame_dump.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace DebugStateType;
|
||||
|
||||
#define C_V(label, value, var, out) \
|
||||
if (Selectable(label, var == value)) { \
|
||||
var = value; \
|
||||
selected_cmd = -1; \
|
||||
out = true; \
|
||||
}
|
||||
|
||||
// 00 to 99
|
||||
static std::array<char, 3> small_int_to_str(const s32 i) {
|
||||
std::array<char, 3> label{};
|
||||
if (i == -1) {
|
||||
label[0] = 'N';
|
||||
label[1] = 'A';
|
||||
} else {
|
||||
label[0] = i / 10 + '0';
|
||||
label[1] = i % 10 + '0';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump)
|
||||
: frame_dump(std::make_shared<FrameDump>(_frame_dump)) {
|
||||
static int unique_id = 0;
|
||||
id = unique_id++;
|
||||
|
||||
selected_queue_type = QueueType::dcb;
|
||||
selected_submit_num = 0;
|
||||
selected_queue_num2 = 0;
|
||||
|
||||
has_queue_type.fill(false);
|
||||
cmd_list_viewer.reserve(frame_dump->queues.size());
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (!cmd.data.empty()) {
|
||||
has_queue_type[static_cast<s32>(cmd.type)] = true;
|
||||
}
|
||||
const auto fname = fmt::format("F{} {}_{:02}_{:02}", frame_dump->frame_id,
|
||||
magic_enum::enum_name(cmd.type), cmd.submit_num, cmd.num2);
|
||||
cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname);
|
||||
if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) {
|
||||
selected_cmd = static_cast<s32>(cmd_list_viewer.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameDumpViewer::~FrameDumpViewer() = default;
|
||||
|
||||
void FrameDumpViewer::Draw() {
|
||||
if (!is_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto try_select = [&, this] {
|
||||
const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) {
|
||||
return cmd.type == selected_queue_type &&
|
||||
(selected_submit_num == -1 || cmd.submit_num == selected_submit_num) &&
|
||||
(selected_queue_num2 == -1 || cmd.num2 == selected_queue_num2);
|
||||
});
|
||||
if (it != frame_dump->queues.end()) {
|
||||
selected_cmd = static_cast<s32>(std::distance(frame_dump->queues.begin(), it));
|
||||
selected_submit_num = static_cast<s32>(frame_dump->queues[selected_cmd].submit_num);
|
||||
selected_queue_num2 = static_cast<s32>(frame_dump->queues[selected_cmd].num2);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_showing = Options.frame_dump_render_on_collapse;
|
||||
bool is_collapsed = true;
|
||||
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "Frame #%d dump", frame_dump->frame_id);
|
||||
if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) {
|
||||
is_showing = true;
|
||||
is_collapsed = false;
|
||||
|
||||
if (IsWindowAppearing()) {
|
||||
auto window = GetCurrentWindow();
|
||||
static ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
SetWindowDock(window, dock_id, ImGuiCond_Once | ImGuiCond_FirstUseEver);
|
||||
SetWindowSize(window, ImVec2{470.0f, 600.0f});
|
||||
}
|
||||
BeginGroup();
|
||||
TextEx("Queue type");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_queue_type", magic_enum::enum_name(selected_queue_type).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
bool selected = false;
|
||||
#define COMBO(x) \
|
||||
if (has_queue_type[static_cast<s32>(x)]) \
|
||||
C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected)
|
||||
COMBO(QueueType::dcb);
|
||||
COMBO(QueueType::ccb);
|
||||
COMBO(QueueType::acb);
|
||||
if (selected) {
|
||||
selected_submit_num = selected_queue_num2 = -1;
|
||||
try_select();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
SameLine();
|
||||
BeginDisabled(selected_cmd == -1);
|
||||
if (SmallButton("Dump cmd")) {
|
||||
auto now_time = fmt::localtime(std::time(nullptr));
|
||||
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
||||
magic_enum::enum_name(selected_queue_type),
|
||||
selected_submit_num, selected_queue_num2);
|
||||
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
|
||||
const auto& data = frame_dump->queues[selected_cmd].data;
|
||||
if (file.IsOpen()) {
|
||||
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
||||
file.Write(data);
|
||||
} else {
|
||||
DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname));
|
||||
LOG_ERROR(Core, "Failed to open file {}", fname);
|
||||
}
|
||||
}
|
||||
EndDisabled();
|
||||
|
||||
TextEx("Submit num");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
std::array<bool, 32> available_submits{false};
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (cmd.type == selected_queue_type && !cmd.data.empty()) {
|
||||
available_submits[cmd.submit_num] = true;
|
||||
}
|
||||
}
|
||||
bool selected = false;
|
||||
for (int i = 0; i < available_submits.size(); ++i) {
|
||||
if (available_submits[i]) {
|
||||
char label[3]{};
|
||||
label[0] = i / 10 + '0';
|
||||
label[1] = i % 10 + '0';
|
||||
C_V(label, i, selected_submit_num, selected);
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
selected_queue_num2 = -1;
|
||||
try_select();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
SameLine();
|
||||
TextEx(selected_queue_type == QueueType::acb ? "Queue num" : "Buffer num");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
std::array<bool, 32> available_queues{false};
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num &&
|
||||
!cmd.data.empty()) {
|
||||
available_queues[cmd.num2] = true;
|
||||
}
|
||||
}
|
||||
bool selected = false;
|
||||
for (int i = 0; i < available_queues.size(); ++i) {
|
||||
if (available_queues[i]) {
|
||||
char label[3]{};
|
||||
label[0] = i / 10 + '0';
|
||||
label[1] = i % 10 + '0';
|
||||
C_V(label, i, selected_queue_num2, selected);
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
try_select();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
if (is_showing && selected_cmd != -1) {
|
||||
cmd_list_viewer[selected_cmd].Draw(is_collapsed);
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
|
||||
#undef C_V
|
||||
40
src/core/devtools/widget/frame_dump.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "cmd_list.h"
|
||||
#include "core/debug_state.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class CmdListViewer;
|
||||
|
||||
class FrameDumpViewer {
|
||||
friend class CmdListViewer;
|
||||
|
||||
std::shared_ptr<DebugStateType::FrameDump> frame_dump;
|
||||
int id;
|
||||
|
||||
std::vector<CmdListViewer> cmd_list_viewer;
|
||||
std::array<bool, 3> has_queue_type;
|
||||
|
||||
DebugStateType::QueueType selected_queue_type;
|
||||
s32 selected_submit_num;
|
||||
s32 selected_queue_num2;
|
||||
s32 selected_cmd = -1;
|
||||
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
explicit FrameDumpViewer(const DebugStateType::FrameDump& frame_dump);
|
||||
|
||||
~FrameDumpViewer();
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
99
src/core/devtools/widget/frame_graph.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "frame_graph.h"
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
constexpr float TARGET_FPS = 60.0f;
|
||||
constexpr float BAR_WIDTH_MULT = 1.4f;
|
||||
constexpr float BAR_HEIGHT_MULT = 1.25f;
|
||||
constexpr float FRAME_GRAPH_PADDING_Y = 3.0f;
|
||||
constexpr static float FRAME_GRAPH_HEIGHT = 50.0f;
|
||||
|
||||
void FrameGraph::Draw() {
|
||||
if (!is_open) {
|
||||
return;
|
||||
}
|
||||
SetNextWindowSize({340.0, 185.0f}, ImGuiCond_FirstUseEver);
|
||||
if (Begin("Video debug info", &is_open)) {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
const auto& window = *ctx.CurrentWindow;
|
||||
auto& draw_list = *window.DrawList;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
static float deltaTime;
|
||||
static float frameRate;
|
||||
|
||||
if (!isSystemPaused) {
|
||||
deltaTime = io.DeltaTime * 1000.0f;
|
||||
frameRate = 1000.0f / deltaTime;
|
||||
}
|
||||
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
|
||||
Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(),
|
||||
DebugState.gnm_frame_count.load());
|
||||
SeparatorText("Frame graph");
|
||||
|
||||
const float full_width = GetContentRegionAvail().x;
|
||||
// Frame graph - inspired by
|
||||
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
|
||||
auto pos = GetCursorScreenPos();
|
||||
const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f};
|
||||
ItemSize(size);
|
||||
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
|
||||
float cur_pos_x = pos.x + full_width;
|
||||
pos.y += FRAME_GRAPH_PADDING_Y;
|
||||
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
|
||||
|
||||
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
|
||||
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
|
||||
IM_COL32(0x33, 0x33, 0x33, 0xFF));
|
||||
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
|
||||
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
|
||||
const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE];
|
||||
const float dt_factor = target_dt / frame_info.delta;
|
||||
|
||||
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
|
||||
const float height =
|
||||
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT;
|
||||
|
||||
ImU32 color;
|
||||
if (dt_factor >= 0.95f) { // BLUE
|
||||
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
|
||||
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
|
||||
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
|
||||
int r = (int)(0xFF * t);
|
||||
color = IM_COL32(r, 0xFF, 0, 0xFF);
|
||||
} else { // YELLOW <> RED
|
||||
float t = dt_factor * 2.0f;
|
||||
int g = (int)(0xFF * t);
|
||||
color = IM_COL32(0xFF, g, 0, 0xFF);
|
||||
}
|
||||
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height},
|
||||
{cur_pos_x, final_pos_y}, color);
|
||||
cur_pos_x -= width;
|
||||
if (cur_pos_x < width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
draw_list.PopClipRect();
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||