Compare commits
249 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fa625c4fa | ||
|
|
770611fdf3 | ||
|
|
07e3c56f0d | ||
|
|
eb4ce48b65 | ||
|
|
1524ff87d2 | ||
|
|
312e5eda66 | ||
|
|
161d696013 | ||
|
|
9981ce8d98 | ||
|
|
40493231ed | ||
|
|
43a1948d58 | ||
|
|
a1815b617c | ||
|
|
908c79881b | ||
|
|
a5bc0bcc66 | ||
|
|
ed0319cfed | ||
|
|
3f3c2dc20f | ||
|
|
78ce053b4d | ||
|
|
8509460d2c | ||
|
|
2c1e2c63c3 | ||
|
|
7b0affb6e0 | ||
|
|
603952bc27 | ||
|
|
3196d957b0 | ||
|
|
4ef66ec8fb | ||
|
|
d41ffb592c | ||
|
|
01bc0c84f0 | ||
|
|
2575a93dc6 | ||
|
|
f5c1d7b8c8 | ||
|
|
86ccce3721 | ||
|
|
38e4a144a1 | ||
|
|
9cafb0d912 | ||
|
|
00b09de3d9 | ||
|
|
a2d29412cb | ||
|
|
846c994cc9 | ||
|
|
096366ead5 | ||
|
|
c78f6d4f20 | ||
|
|
c34a95fa25 | ||
|
|
b5d6194f6d | ||
|
|
a5e419535f | ||
|
|
9775fae4eb | ||
|
|
a262dc02b5 | ||
|
|
fca5752690 | ||
|
|
7b48e7b363 | ||
|
|
a7d9be1384 | ||
|
|
abfd690601 | ||
|
|
bf7e78795f | ||
|
|
a14438d013 | ||
|
|
48737a4bb2 | ||
|
|
b321c39371 | ||
|
|
19f475fd70 | ||
|
|
2c56e94702 | ||
|
|
95b844dbae | ||
|
|
9da4e62573 | ||
|
|
1c8f6ba18f | ||
|
|
31c6ba7ecd | ||
|
|
ab0e71d7cb | ||
|
|
1fd194141a | ||
|
|
737c446fc1 | ||
|
|
73e13aa090 | ||
|
|
0d5792cc57 | ||
|
|
f37b2e6f10 | ||
|
|
24d7aaf43c | ||
|
|
5b2b15091f | ||
|
|
c42fde2a37 | ||
|
|
fef3d8acb5 | ||
|
|
e56410b404 | ||
|
|
a6371fb69d | ||
|
|
a33e7c13fa | ||
|
|
945f3222ae | ||
|
|
9e384ed54b | ||
|
|
561f5c9c14 | ||
|
|
cf7e4bda92 | ||
|
|
208ed712f4 | ||
|
|
d1f2f5f146 | ||
|
|
744a208763 | ||
|
|
f86b770ff7 | ||
|
|
0ae4eae9a6 | ||
|
|
25429998e3 | ||
|
|
5ace5c1b7a | ||
|
|
23514388ed | ||
|
|
f117351783 | ||
|
|
4572634a4e | ||
|
|
103997ee56 | ||
|
|
c9de5474bf | ||
|
|
a7358ff1d4 | ||
|
|
20eab9fed9 | ||
|
|
7620e1a631 | ||
|
|
0eeee431dc | ||
|
|
888f499188 | ||
|
|
c6e7ca562a | ||
|
|
a9b4dd022c | ||
|
|
5568763a57 | ||
|
|
a3b12e3809 | ||
|
|
742f021fdf | ||
|
|
95bcf6ac38 | ||
|
|
e371961219 | ||
|
|
5503338f21 | ||
|
|
fe7184c2a8 | ||
|
|
1c83014526 | ||
|
|
2d903e3ce6 | ||
|
|
e29e8eec2f | ||
|
|
dc47d0f624 | ||
|
|
8b55f2c615 | ||
|
|
fcfe192e83 | ||
|
|
bd38aefc57 | ||
|
|
feaf010fa2 | ||
|
|
ebecdd3a74 | ||
|
|
a29ddcee40 | ||
|
|
d11547024c | ||
|
|
6f59e2676b | ||
|
|
8fea7e56e5 | ||
|
|
58fea44eb5 | ||
|
|
084d7d6b01 | ||
|
|
bd3bfe411d | ||
|
|
963ed37fd6 | ||
|
|
741da9c8bf | ||
|
|
69d92a19a5 | ||
|
|
8671aa8dd0 | ||
|
|
efc89c032b | ||
|
|
d0328f49f1 | ||
|
|
c1bd602e4c | ||
|
|
b3d6f7bdd8 | ||
|
|
12156b199a | ||
|
|
a0407a8e64 | ||
|
|
7582717c9d | ||
|
|
ec85eac3c9 | ||
|
|
fb4b507ba4 | ||
|
|
7ea78699a1 | ||
|
|
80ad90651e | ||
|
|
b94739cfa7 | ||
|
|
89e00c442d | ||
|
|
d796341d33 | ||
|
|
5282efac1b | ||
|
|
ae83d5c6d3 | ||
|
|
3370546a7a | ||
|
|
2ff606628c | ||
|
|
20576ebb43 | ||
|
|
6f81160160 | ||
|
|
266e086706 | ||
|
|
9561a2f5b1 | ||
|
|
bc8699a9fa | ||
|
|
c3cc65a11e | ||
|
|
1f0fee33ed | ||
|
|
de6c0defb3 | ||
|
|
6c659c3a16 | ||
|
|
af022294dd | ||
|
|
073714a762 | ||
|
|
4ae75bec50 | ||
|
|
31527ccd25 | ||
|
|
268878f895 | ||
|
|
d00b7be2d6 | ||
|
|
28877cea31 | ||
|
|
941b663352 | ||
|
|
708e5b027f | ||
|
|
c33c9c76bf | ||
|
|
888e814130 | ||
|
|
cad53179ed | ||
|
|
3c313a43fd | ||
|
|
45bdbf538c | ||
|
|
4544407af6 | ||
|
|
2f2e443858 | ||
|
|
14db101148 | ||
|
|
4dd6bcd206 | ||
|
|
ea89cf8639 | ||
|
|
5c0a31e29f | ||
|
|
07922abffc | ||
|
|
114a4562ed | ||
|
|
858f8ac6d9 | ||
|
|
b71130e6f1 | ||
|
|
054732210e | ||
|
|
af418eb666 | ||
|
|
a2f6a2480d | ||
|
|
503feba7e4 | ||
|
|
69511aed3d | ||
|
|
989d4a7a41 | ||
|
|
a32f6e9d8e | ||
|
|
36df3ce97e | ||
|
|
de2f2e5140 | ||
|
|
fb4b3c127f | ||
|
|
72b34650f9 | ||
|
|
105c60b984 | ||
|
|
e609bc1c6a | ||
|
|
f6c47df671 | ||
|
|
422525e3fb | ||
|
|
2dafb27055 | ||
|
|
500b01076e | ||
|
|
b43ae9d5ed | ||
|
|
f22867efc5 | ||
|
|
be351c2b3d | ||
|
|
67fa743414 | ||
|
|
5799fa4d7d | ||
|
|
499c89790b | ||
|
|
f3d3528754 | ||
|
|
ce961cb4c6 | ||
|
|
75bf2c20eb | ||
|
|
1c8b509441 | ||
|
|
cc289a8ed0 | ||
|
|
017a18f42e | ||
|
|
8a858c2623 | ||
|
|
6e12eb80a0 | ||
|
|
d879741ec6 | ||
|
|
439e621674 | ||
|
|
a1f2610522 | ||
|
|
240f59a4c8 | ||
|
|
c889a5805e | ||
|
|
762a30d0db | ||
|
|
390d49c5f1 | ||
|
|
3cf15af31e | ||
|
|
7aa1d10655 | ||
|
|
74d1b9a254 | ||
|
|
b01541f4e4 | ||
|
|
0e5a6676c3 | ||
|
|
a9beb4746e | ||
|
|
79352ee9d5 | ||
|
|
3a8c2ae328 | ||
|
|
7f53cd1e96 | ||
|
|
4118aad491 | ||
|
|
bf948b5790 | ||
|
|
c82806f9cb | ||
|
|
4eb7f6c044 | ||
|
|
669a9a644d | ||
|
|
b57df1dcb9 | ||
|
|
020982508d | ||
|
|
775e29efb6 | ||
|
|
5035df27c3 | ||
|
|
8f3098fc1e | ||
|
|
3ca3254b9f | ||
|
|
f7908eebb9 | ||
|
|
ffd3afcf2f | ||
|
|
5808e76fae | ||
|
|
5c20373db3 | ||
|
|
f981e90af3 | ||
|
|
55b0dda57c | ||
|
|
36d44bf52f | ||
|
|
370d3fa1e3 | ||
|
|
b3f8c7ce58 | ||
|
|
eea68eae18 | ||
|
|
0b9ef3c0b8 | ||
|
|
2fb6df3fe9 | ||
|
|
dca63391b6 | ||
|
|
c2b583c911 | ||
|
|
e7ba9fd7e1 | ||
|
|
e158951695 | ||
|
|
f345ffdc0f | ||
|
|
26e5bc6082 | ||
|
|
cc6a4bedfc | ||
|
|
b317f54f22 | ||
|
|
4f1a2c2562 | ||
|
|
babd580c64 | ||
|
|
7eb0992d2c | ||
|
|
535bc61b4c |
@@ -3,14 +3,12 @@
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
cmake .. -GNinja -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
|
||||
make -j$(nproc)
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/clang/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh $1
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh "$1"
|
||||
sudo chown -R $UID ./
|
||||
|
||||
0
.ci/scripts/clang/upload.sh
Normal file → Executable file
0
.ci/scripts/clang/upload.sh
Normal file → Executable file
@@ -4,8 +4,10 @@
|
||||
cp license.txt "$DIR_NAME"
|
||||
cp README.md "$DIR_NAME"
|
||||
|
||||
tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md license.txt
|
||||
cp "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
if [[ -z "${NO_SOURCE_PACK}" ]]; then
|
||||
tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md license.txt
|
||||
cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
fi
|
||||
|
||||
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
chmod a+x ./.ci/scripts/format/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -v $(pwd):/yuzu yuzuemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.ci/scripts/format/docker.sh
|
||||
docker run -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.ci/scripts/format/docker.sh
|
||||
sudo chown -R $UID ./
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
@@ -19,15 +17,16 @@ cmake .. \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||
-GNinja
|
||||
|
||||
make -j$(nproc)
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
make install DESTDIR=AppDir
|
||||
DESTDIR="$PWD/AppDir" ninja install
|
||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||
|
||||
# Download tools needed to build an AppImage
|
||||
|
||||
@@ -4,5 +4,5 @@ mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/linux/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh $1
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1"
|
||||
sudo chown -R $UID ./
|
||||
|
||||
5
.ci/scripts/linux/upload.sh
Normal file → Executable file
5
.ci/scripts/linux/upload.sh
Normal file → Executable file
@@ -24,6 +24,11 @@ cd build
|
||||
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/appimagetool-x86_64.AppImage
|
||||
chmod 755 appimagetool-x86_64.AppImage
|
||||
|
||||
# if FUSE is not available, then fallback to extract and run
|
||||
if ! ./appimagetool-x86_64.AppImage --version; then
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
fi
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
# Generate update information if releasing to mainline
|
||||
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
cd /yuzu
|
||||
set -e
|
||||
|
||||
ccache -s
|
||||
#cd /yuzu
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||
ninja
|
||||
ccache -sv
|
||||
|
||||
ccache -s
|
||||
mkdir -p "$HOME/.conan/profiles"
|
||||
wget -c "https://github.com/yuzu-emu/build-environments/raw/master/linux-mingw/default" -O "$HOME/.conan/profiles/default"
|
||||
wget -c "https://github.com/yuzu-emu/build-environments/raw/master/linux-mingw/settings.yml" -O "$HOME/.conan/settings.yml"
|
||||
|
||||
mkdir -p build && cd build
|
||||
export LDFLAGS="-fuse-ld=lld"
|
||||
# -femulated-tls required due to an incompatibility between GCC and Clang
|
||||
# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate
|
||||
export CXXFLAGS="-femulated-tls"
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWClangCross.cmake" \
|
||||
-DDISPLAY_VERSION="$1" \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_CCACHE=ON \
|
||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||
-GNinja
|
||||
ninja yuzu yuzu-cmd
|
||||
|
||||
ccache -sv
|
||||
|
||||
echo "Tests skipped"
|
||||
#ctest -VV -C Release
|
||||
|
||||
@@ -4,5 +4,5 @@ mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/windows/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh $1
|
||||
docker run -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh "$1"
|
||||
sudo chown -R $UID ./
|
||||
|
||||
0
.ci/scripts/windows/upload.sh
Normal file → Executable file
0
.ci/scripts/windows/upload.sh
Normal file → Executable file
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
container: yuzuemu/build-environments:linux-transifex
|
||||
if: ${{ github.repository == 'yuzu-emu/yuzu' && !github.head_ref }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
115
.github/workflows/verify.yml
vendored
Normal file
115
.github/workflows/verify.yml
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
name: 'yuzu verify'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
format:
|
||||
name: 'verify format'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: yuzuemu/build-environments:linux-clang-format
|
||||
options: -u 1001
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: false
|
||||
- name: 'Verify Formatting'
|
||||
run: bash -ex ./.ci/scripts/format/script.sh
|
||||
build:
|
||||
name: 'test build'
|
||||
needs: format
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- type: clang
|
||||
image: linux-fresh
|
||||
- type: linux
|
||||
image: linux-fresh
|
||||
- type: windows
|
||||
image: linux-mingw
|
||||
container:
|
||||
image: yuzuemu/build-environments:${{ matrix.image }}
|
||||
options: -u 1001
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
id: ccache-restore
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.type }}-
|
||||
- name: Create ccache directory
|
||||
if: steps.ccache-restore.outputs.cache-hit != 'true'
|
||||
run: mkdir -p ~/.ccache
|
||||
- name: Build
|
||||
run: ./.ci/scripts/${{ matrix.type }}/docker.sh
|
||||
env:
|
||||
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
||||
- name: Pack
|
||||
run: ./.ci/scripts/${{ matrix.type }}/upload.sh
|
||||
env:
|
||||
NO_SOURCE_PACK: "YES"
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.type }}
|
||||
path: artifacts/
|
||||
build-msvc:
|
||||
name: 'test build (windows, msvc)'
|
||||
needs: format
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.buildcache
|
||||
key: ${{ runner.os }}-msvc-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-msvc-
|
||||
- name: Install dependencies
|
||||
# due to how chocolatey works, only cmd.exe is supported here
|
||||
shell: cmd
|
||||
run: |
|
||||
choco install vulkan-sdk wget
|
||||
python -m pip install --upgrade pip conan
|
||||
call refreshenv
|
||||
wget https://github.com/mbitsnbites/buildcache/releases/download/v0.27.6/buildcache-windows.zip
|
||||
7z x buildcache-windows.zip
|
||||
copy buildcache\bin\buildcache.exe C:\ProgramData\chocolatey\bin
|
||||
rmdir buildcache
|
||||
echo %PATH% >> %GITHUB_PATH%
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Configure
|
||||
env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
run: |
|
||||
glslangValidator --version
|
||||
mkdir build
|
||||
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Cache Summary
|
||||
run: buildcache -s
|
||||
- name: Pack
|
||||
shell: pwsh
|
||||
run: .\.ci\scripts\windows\upload.ps1
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc
|
||||
path: artifacts/
|
||||
@@ -218,8 +218,13 @@ if (Boost_FOUND)
|
||||
list(APPEND Boost_LIBRARIES Boost::context)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
message(STATUS "Boost 1.79.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.79.0")
|
||||
endif()
|
||||
|
||||
# boost:asio has functions that require AcceptEx et al
|
||||
if (MINGW)
|
||||
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
@@ -622,6 +627,14 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
|
||||
-DBOOST_DATE_TIME_NO_LIB
|
||||
-DBOOST_REGEX_NO_LIB
|
||||
)
|
||||
# Adjustments for MSVC + Ninja
|
||||
if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
add_compile_options(
|
||||
/wd4711 # function 'function' selected for automatic inline expansion
|
||||
/wd4464 # relative include path contains '..'
|
||||
/wd4820 # 'identifier1': '4' bytes padding added after data member 'identifier2'
|
||||
)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(externals)
|
||||
|
||||
@@ -2,5 +2,6 @@ function(copy_yuzu_FFmpeg_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
|
||||
string(STRIP "${FFmpeg_REQUIRED_DLLS}" FFmpeg_REQUIRED_DLLS)
|
||||
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
|
||||
endfunction(copy_yuzu_FFmpeg_deps)
|
||||
|
||||
12
CMakeModules/MSVCCache.cmake
Normal file
12
CMakeModules/MSVCCache.cmake
Normal file
@@ -0,0 +1,12 @@
|
||||
# buildcache wrapper
|
||||
OPTION(USE_CCACHE "Use buildcache for compilation" OFF)
|
||||
IF(USE_CCACHE)
|
||||
FIND_PROGRAM(CCACHE buildcache)
|
||||
IF (CCACHE)
|
||||
MESSAGE(STATUS "Using buildcache found in PATH")
|
||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
ELSE(CCACHE)
|
||||
MESSAGE(WARNING "USE_CCACHE enabled, but no buildcache executable found")
|
||||
ENDIF(CCACHE)
|
||||
ENDIF(USE_CCACHE)
|
||||
55
CMakeModules/MinGWClangCross.cmake
Normal file
55
CMakeModules/MinGWClangCross.cmake
Normal file
@@ -0,0 +1,55 @@
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
|
||||
set(SDL2_PATH ${MINGW_PREFIX})
|
||||
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
|
||||
|
||||
# Specify the cross compiler
|
||||
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}clang)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
|
||||
# Mingw tools
|
||||
set(STRIP ${MINGW_TOOL_PREFIX}strip)
|
||||
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
|
||||
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
|
||||
|
||||
# ccache wrapper
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache found in PATH")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else(CCACHE)
|
||||
message(WARNING "USE_CCACHE enabled, but no ccache found")
|
||||
endif(CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
# Search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
|
||||
# Echo modified cmake vars to screen for debugging purposes
|
||||
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
|
||||
message("")
|
||||
message("Custom cmake vars: (blank = system default)")
|
||||
message("-----------------------------------------")
|
||||
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
|
||||
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
|
||||
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
|
||||
message("* WINDRES : ${WINDRES}")
|
||||
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
|
||||
message("* STRIP : ${STRIP}")
|
||||
message("* USE_CCACHE : ${USE_CCACHE}")
|
||||
message("")
|
||||
# So that the debug info only appears once
|
||||
set(ENV{MINGW_DEBUG_INFO} SHOWN)
|
||||
endif()
|
||||
879
dist/languages/ca.ts
vendored
879
dist/languages/ca.ts
vendored
File diff suppressed because it is too large
Load Diff
940
dist/languages/cs.ts
vendored
940
dist/languages/cs.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/da.ts
vendored
879
dist/languages/da.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/de.ts
vendored
879
dist/languages/de.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/el.ts
vendored
879
dist/languages/el.ts
vendored
File diff suppressed because it is too large
Load Diff
892
dist/languages/es.ts
vendored
892
dist/languages/es.ts
vendored
File diff suppressed because it is too large
Load Diff
6235
dist/languages/fi.ts
vendored
Normal file
6235
dist/languages/fi.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1001
dist/languages/fr.ts
vendored
1001
dist/languages/fr.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/id.ts
vendored
879
dist/languages/id.ts
vendored
File diff suppressed because it is too large
Load Diff
891
dist/languages/it.ts
vendored
891
dist/languages/it.ts
vendored
File diff suppressed because it is too large
Load Diff
1177
dist/languages/ja_JP.ts
vendored
1177
dist/languages/ja_JP.ts
vendored
File diff suppressed because it is too large
Load Diff
895
dist/languages/ko_KR.ts
vendored
895
dist/languages/ko_KR.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/nb.ts
vendored
879
dist/languages/nb.ts
vendored
File diff suppressed because it is too large
Load Diff
885
dist/languages/nl.ts
vendored
885
dist/languages/nl.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/pl.ts
vendored
879
dist/languages/pl.ts
vendored
File diff suppressed because it is too large
Load Diff
896
dist/languages/pt_BR.ts
vendored
896
dist/languages/pt_BR.ts
vendored
File diff suppressed because it is too large
Load Diff
895
dist/languages/pt_PT.ts
vendored
895
dist/languages/pt_PT.ts
vendored
File diff suppressed because it is too large
Load Diff
919
dist/languages/ru_RU.ts
vendored
919
dist/languages/ru_RU.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/sv.ts
vendored
879
dist/languages/sv.ts
vendored
File diff suppressed because it is too large
Load Diff
905
dist/languages/tr_TR.ts
vendored
905
dist/languages/tr_TR.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/vi.ts
vendored
879
dist/languages/vi.ts
vendored
File diff suppressed because it is too large
Load Diff
879
dist/languages/vi_VN.ts
vendored
879
dist/languages/vi_VN.ts
vendored
File diff suppressed because it is too large
Load Diff
881
dist/languages/zh_CN.ts
vendored
881
dist/languages/zh_CN.ts
vendored
File diff suppressed because it is too large
Load Diff
883
dist/languages/zh_TW.ts
vendored
883
dist/languages/zh_TW.ts
vendored
File diff suppressed because it is too large
Load Diff
5
dist/org.yuzu_emu.yuzu.metainfo.xml
vendored
5
dist/org.yuzu_emu.yuzu.metainfo.xml
vendored
@@ -32,6 +32,11 @@
|
||||
<binary>yuzu</binary>
|
||||
<binary>yuzu-cmd</binary>
|
||||
</provides>
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>gamepad</control>
|
||||
</supports>
|
||||
<requires>
|
||||
<memory>8192</memory>
|
||||
</requires>
|
||||
|
||||
BIN
dist/qt_themes/default/icons/256x256/yuzu.png
vendored
BIN
dist/qt_themes/default/icons/256x256/yuzu.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.6 KiB |
13
dist/qt_themes/default/style.qss
vendored
13
dist/qt_themes/default/style.qss
vendored
@@ -58,6 +58,19 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #109010;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #000000;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
|
||||
13
dist/qt_themes/qdarkstyle/style.qss
vendored
13
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -1304,6 +1304,19 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 23px;
|
||||
min-height: 23px;
|
||||
|
||||
@@ -2207,6 +2207,19 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 19px;
|
||||
min-height: 19px;
|
||||
|
||||
5
externals/CMakeLists.txt
vendored
5
externals/CMakeLists.txt
vendored
@@ -40,6 +40,11 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
|
||||
# GCC bugs
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND MINGW)
|
||||
target_compile_options(microprofile INTERFACE "-Wno-array-bounds")
|
||||
endif()
|
||||
|
||||
# libusb
|
||||
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
|
||||
add_subdirectory(libusb)
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: e005e1f817...33d4dd987f
2
externals/cpp-httplib
vendored
2
externals/cpp-httplib
vendored
Submodule externals/cpp-httplib updated: 9648f950f5...305a7abcb9
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 28714ee75a...7f84870712
2
externals/libressl
vendored
2
externals/libressl
vendored
Submodule externals/libressl updated: 8289d0d07d...8929f818fd
2
externals/microprofile/microprofile.h
vendored
2
externals/microprofile/microprofile.h
vendored
@@ -1246,7 +1246,7 @@ struct MicroProfileScopeLock
|
||||
{
|
||||
bool bUseLock;
|
||||
std::recursive_mutex& m;
|
||||
MicroProfileScopeLock(std::recursive_mutex& m) : bUseLock(g_bUseLock), m(m)
|
||||
MicroProfileScopeLock(std::recursive_mutex& m_) : bUseLock(g_bUseLock), m(m_)
|
||||
{
|
||||
if(bUseLock)
|
||||
m.lock();
|
||||
|
||||
75
externals/microprofile/microprofileui.h
vendored
75
externals/microprofile/microprofileui.h
vendored
@@ -213,8 +213,8 @@ struct MicroProfileCustom
|
||||
|
||||
struct SOptionDesc
|
||||
{
|
||||
SOptionDesc(){}
|
||||
SOptionDesc(uint8_t nSubType, uint8_t nIndex, const char* fmt, ...):nSubType(nSubType), nIndex(nIndex)
|
||||
SOptionDesc()=default;
|
||||
SOptionDesc(uint8_t nSubType_, uint8_t nIndex_, const char* fmt, ...):nSubType(nSubType_), nIndex(nIndex_)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
@@ -573,10 +573,10 @@ inline void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
|
||||
for(int k = 0; k < MICROPROFILE_META_MAX; ++k)
|
||||
{
|
||||
nMetaSumInclusive[i] += nMetaSum[i];
|
||||
nMetaSum[i] = 0;
|
||||
nMetaSumInclusive[k] += nMetaSum[k];
|
||||
nMetaSum[k] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -708,10 +708,10 @@ inline void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nTok
|
||||
|
||||
if(UI.nMouseLeftMod)
|
||||
{
|
||||
int nIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
|
||||
g_MicroProfileUI.nLockedToolTipColor[nIndex] = S.TimerInfo[nTimerId].nColor;
|
||||
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nIndex], &ToolTip);
|
||||
g_MicroProfileUI.LockedToolTipFront = nIndex;
|
||||
int nToolTipIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
|
||||
g_MicroProfileUI.nLockedToolTipColor[nToolTipIndex] = S.TimerInfo[nTimerId].nColor;
|
||||
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nToolTipIndex], &ToolTip);
|
||||
g_MicroProfileUI.LockedToolTipFront = nToolTipIndex;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -917,9 +917,8 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
float fStart = floor(fMsBase*fRcpStep) * fStep;
|
||||
for(float f = fStart; f < fMsEnd; )
|
||||
{
|
||||
float fStart = f;
|
||||
float fNext = f + fStep;
|
||||
MicroProfileDrawBox(((fStart-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
|
||||
MicroProfileDrawBox(((f-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
|
||||
f = fNext;
|
||||
}
|
||||
}
|
||||
@@ -1116,9 +1115,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
|
||||
nMaxStackDepth = MicroProfileMax(nMaxStackDepth, nStackPos);
|
||||
float fMsStart = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickStart);
|
||||
float fMsEnd = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
|
||||
float fMsEnd2 = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
float fXEnd = fMsEnd2 * fMsToScreen;
|
||||
float fYStart = (float)(nY + nStackPos * nYDelta);
|
||||
float fYEnd = fYStart + (MICROPROFILE_DETAILED_BAR_HEIGHT);
|
||||
float fXDist = MicroProfileMax(fXStart - fMouseX, fMouseX - fXEnd);
|
||||
@@ -1269,22 +1268,22 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
if(UI.nRangeBegin != UI.nRangeEnd)
|
||||
{
|
||||
float fMsStart = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeBegin);
|
||||
float fMsEnd = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
|
||||
float fMsEnd3 = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
float fXEnd = fMsEnd3 * fMsToScreen;
|
||||
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
|
||||
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
|
||||
|
||||
fMsStart += fDetailedOffset;
|
||||
fMsEnd += fDetailedOffset;
|
||||
fMsEnd3 += fDetailedOffset;
|
||||
char sBuffer[32];
|
||||
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
|
||||
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
|
||||
float fStartTextX = fXStart - fStartTextWidth - 2;
|
||||
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd3);
|
||||
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
|
||||
|
||||
@@ -1297,9 +1296,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
if(UI.nRangeBeginGpu != UI.nRangeEndGpu)
|
||||
{
|
||||
float fMsStart = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeBeginGpu);
|
||||
float fMsEnd = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
|
||||
float fMsEnd4 = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
float fXEnd = fMsEnd4 * fMsToScreen;
|
||||
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
|
||||
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
|
||||
@@ -1307,14 +1306,14 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
nBaseY += MICROPROFILE_TEXT_HEIGHT+1;
|
||||
|
||||
fMsStart += fDetailedOffset;
|
||||
fMsEnd += fDetailedOffset;
|
||||
fMsEnd4 += fDetailedOffset;
|
||||
char sBuffer[32];
|
||||
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
|
||||
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
|
||||
float fStartTextX = fXStart - fStartTextWidth - 2;
|
||||
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd4);
|
||||
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
|
||||
}
|
||||
@@ -1716,8 +1715,8 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
uint32_t nTextCount = 0;
|
||||
uint32_t nGraphIndex = (S.nGraphPut + MICROPROFILE_GRAPH_HISTORY - int(MICROPROFILE_GRAPH_HISTORY*(1.f - fMouseXPrc))) % MICROPROFILE_GRAPH_HISTORY;
|
||||
|
||||
uint32_t nX = UI.nMouseX;
|
||||
uint32_t nY = UI.nMouseY + 20;
|
||||
uint32_t nMouseX = UI.nMouseX;
|
||||
uint32_t nMouseY = UI.nMouseY + 20;
|
||||
|
||||
for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
|
||||
{
|
||||
@@ -1736,7 +1735,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
}
|
||||
if(nTextCount)
|
||||
{
|
||||
MicroProfileDrawFloatWindow(nX, nY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
|
||||
MicroProfileDrawFloatWindow(nMouseX, nMouseY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
|
||||
}
|
||||
|
||||
if(UI.nMouseRight)
|
||||
@@ -2321,8 +2320,8 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
uint32_t nMenuX[MICROPROFILE_MENU_MAX] = {0};
|
||||
uint32_t nNumMenuItems = 0;
|
||||
|
||||
int nLen = snprintf(buffer, 127, "MicroProfile");
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
|
||||
int nMPTextLen = snprintf(buffer, 127, "MicroProfile");
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nMPTextLen);
|
||||
nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
pMenuText[nNumMenuItems++] = "Mode";
|
||||
pMenuText[nNumMenuItems++] = "Groups";
|
||||
@@ -2438,16 +2437,16 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
int nNumLines = 0;
|
||||
bool bSelected = false;
|
||||
const char* pString = CB(nNumLines, &bSelected);
|
||||
uint32_t nWidth = 0, nHeight = 0;
|
||||
uint32_t nTextWidth = 0, nTextHeight = 0;
|
||||
while(pString)
|
||||
{
|
||||
nWidth = MicroProfileMax<int>(nWidth, (int)strlen(pString));
|
||||
nTextWidth = MicroProfileMax<int>(nTextWidth, (int)strlen(pString));
|
||||
nNumLines++;
|
||||
pString = CB(nNumLines, &bSelected);
|
||||
}
|
||||
nWidth = (2+nWidth) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
nHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
|
||||
if(UI.nMouseY <= nY + nHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nWidth + 0 && UI.nMouseX >= nX - 0)
|
||||
nTextWidth = (2+nTextWidth) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
nTextHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
|
||||
if(UI.nMouseY <= nY + nTextHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nTextWidth + 0 && UI.nMouseX >= nX - 0)
|
||||
{
|
||||
UI.nActiveMenu = nMenu;
|
||||
}
|
||||
@@ -2455,21 +2454,21 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
{
|
||||
UI.nActiveMenu = UINT32_MAX;
|
||||
}
|
||||
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
|
||||
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + nTextHeight, 0xff000000|g_nMicroProfileBackColors[1]);
|
||||
for(int i = 0; i < nNumLines; ++i)
|
||||
{
|
||||
bool bSelected = false;
|
||||
const char* pString = CB(i, &bSelected);
|
||||
bool bSelected2 = false;
|
||||
const char* pString2 = CB(i, &bSelected2);
|
||||
if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
|
||||
{
|
||||
if(UI.nMouseLeft || UI.nMouseRight)
|
||||
{
|
||||
CBClick[nMenu](i);
|
||||
}
|
||||
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
|
||||
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
|
||||
}
|
||||
int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
|
||||
int nTextLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected2 ? '*' : ' ' ,pString2);
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nTextLen);
|
||||
nY += MICROPROFILE_TEXT_HEIGHT+1;
|
||||
}
|
||||
}
|
||||
@@ -2605,7 +2604,7 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
|
||||
for(uint32_t i = 0; i < nCount; ++i)
|
||||
{
|
||||
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
|
||||
uint32_t nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
|
||||
nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
|
||||
MicroProfileDrawBox(nMaxOffsetX, nOffsetY, nMaxOffsetX+nWidth, nOffsetY+MICROPROFILE_TEXT_HEIGHT, pColors[i]|0xff000000);
|
||||
}
|
||||
}
|
||||
|
||||
2
externals/sirit
vendored
2
externals/sirit
vendored
Submodule externals/sirit updated: a39596358a...aa292d5665
1
externals/soundtouch
vendored
1
externals/soundtouch
vendored
Submodule externals/soundtouch deleted from 060181eaf2
@@ -36,7 +36,6 @@ if (MSVC)
|
||||
# /GT - Supports fiber safety for data allocated using static thread-local storage
|
||||
add_compile_options(
|
||||
/MP
|
||||
/Zi
|
||||
/Zm200
|
||||
/Zo
|
||||
/permissive-
|
||||
@@ -65,6 +64,10 @@ if (MSVC)
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
/we4388 # 'expression': signed/unsigned mismatch
|
||||
/we4389 # 'operator': signed/unsigned mismatch
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
/we4505 # 'function': unreferenced local function has been removed
|
||||
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
@@ -75,6 +78,13 @@ if (MSVC)
|
||||
/we5245 # 'function': unreferenced function with internal linkage has been removed
|
||||
)
|
||||
|
||||
if (USE_CCACHE)
|
||||
# when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format
|
||||
add_compile_options(/Z7)
|
||||
else()
|
||||
add_compile_options(/Zi)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options(/QIntel-jcc-erratum)
|
||||
endif()
|
||||
@@ -92,6 +102,7 @@ else()
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
|
||||
@@ -49,9 +49,6 @@ if (NOT MSVC)
|
||||
target_compile_options(audio_core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=shadow
|
||||
-Werror=unused-parameter
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
@@ -98,13 +98,13 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor
|
||||
|
||||
AudioRenderer::~AudioRenderer() = default;
|
||||
|
||||
ResultCode AudioRenderer::Start() {
|
||||
Result AudioRenderer::Start() {
|
||||
audio_out->StartStream(stream);
|
||||
ReleaseAndQueueBuffers();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode AudioRenderer::Stop() {
|
||||
Result AudioRenderer::Stop() {
|
||||
audio_out->StopStream(stream);
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -125,8 +125,8 @@ Stream::State AudioRenderer::GetStreamState() const {
|
||||
return stream->GetState();
|
||||
}
|
||||
|
||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params) {
|
||||
Result AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params) {
|
||||
std::scoped_lock lock{mutex};
|
||||
InfoUpdater info_updater{input_params, output_params, behavior_info};
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ public:
|
||||
Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
[[nodiscard]] ResultCode Start();
|
||||
[[nodiscard]] ResultCode Stop();
|
||||
[[nodiscard]] Result UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
[[nodiscard]] Result Start();
|
||||
[[nodiscard]] Result Stop();
|
||||
void QueueMixedBuffer(Buffer::Tag tag);
|
||||
void ReleaseAndQueueBuffers();
|
||||
[[nodiscard]] u32 GetSampleRate() const;
|
||||
|
||||
@@ -129,17 +129,17 @@ s32 ToS32(float sample) {
|
||||
return static_cast<s32>(rescaled_sample);
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
template <std::size_t CHANNEL_COUNT>
|
||||
void ApplyReverbGeneric(
|
||||
@@ -429,7 +429,7 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
|
||||
in_params.node_id);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
|
||||
ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1312,7 +1312,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::s
|
||||
samples_to_read - samples_read, channel, temp_mix_offset);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
|
||||
ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
|
||||
}
|
||||
|
||||
temp_mix_offset += samples_decoded;
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
namespace AudioCommon {
|
||||
namespace Audren {
|
||||
constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
constexpr Result ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr Result ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
} // namespace Audren
|
||||
|
||||
constexpr u8 BASE_REVISION = '0';
|
||||
|
||||
@@ -50,7 +50,7 @@ EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
|
||||
effects[i] = std::make_unique<EffectBiquadFilter>();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented effect {}", effect);
|
||||
ASSERT_MSG(false, "Unimplemented effect {}", effect);
|
||||
effects[i] = std::make_unique<EffectStubbed>();
|
||||
}
|
||||
return GetInfo(i);
|
||||
@@ -104,7 +104,7 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||
auto& params = GetParams();
|
||||
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
|
||||
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
|
||||
ASSERT_MSG(false, "Invalid reverb max channel count {}", reverb_params->max_channels);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,9 +285,8 @@ bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context,
|
||||
EffectContext& effect_context) {
|
||||
Result InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context) {
|
||||
std::vector<MixInfo::InParams> mix_in_params;
|
||||
|
||||
if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
||||
|
||||
@@ -32,8 +32,8 @@ public:
|
||||
VAddr audio_codec_dsp_addr);
|
||||
bool UpdateEffects(EffectContext& effect_context, bool is_active);
|
||||
bool UpdateSplitterInfo(SplitterContext& splitter_context);
|
||||
ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context);
|
||||
Result UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context);
|
||||
bool UpdateSinks(SinkContext& sink_context);
|
||||
bool UpdatePerformanceBuffer();
|
||||
bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
|
||||
|
||||
@@ -483,7 +483,7 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
|
||||
// Add more work
|
||||
index_stack.push(j);
|
||||
} else if (node_state == NodeStates::State::InFound) {
|
||||
UNREACHABLE_MSG("Node start marked as found");
|
||||
ASSERT_MSG(false, "Node start marked as found");
|
||||
ResetState();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
|
||||
in_params.current_playstate = ServerPlayState::Play;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
|
||||
ASSERT_MSG(false, "Unknown playstate {}", voice_in.play_state);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
|
||||
return in_params.should_depop;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
|
||||
ASSERT_MSG(false, "Invalid playstate {}", in_params.current_playstate);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -58,6 +58,7 @@ add_library(common STATIC
|
||||
div_ceil.h
|
||||
dynamic_library.cpp
|
||||
dynamic_library.h
|
||||
elf.h
|
||||
error.cpp
|
||||
error.h
|
||||
expected.h
|
||||
|
||||
@@ -6,8 +6,13 @@
|
||||
|
||||
#include "common/settings.h"
|
||||
|
||||
void assert_handle_failure() {
|
||||
void assert_fail_impl() {
|
||||
if (Settings::values.use_debug_asserts) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void unreachable_impl() {
|
||||
Crash();
|
||||
throw std::runtime_error("Unreachable code");
|
||||
}
|
||||
|
||||
@@ -9,44 +9,43 @@
|
||||
// Sometimes we want to try to continue even after hitting an assert.
|
||||
// However touching this file yields a global recompilation as this header is included almost
|
||||
// everywhere. So let's just move the handling of the failed assert to a single cpp file.
|
||||
void assert_handle_failure();
|
||||
|
||||
// For asserts we'd like to keep all the junk executed when an assert happens away from the
|
||||
// important code in the function. One way of doing this is to put all the relevant code inside a
|
||||
// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
|
||||
// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
|
||||
// template that calls the lambda. This seems to generate an extra instruction at the call-site
|
||||
// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
|
||||
// enough for our purposes.
|
||||
template <typename Fn>
|
||||
#if defined(_MSC_VER)
|
||||
[[msvc::noinline]]
|
||||
#elif defined(__GNUC__)
|
||||
[[gnu::cold, gnu::noinline]]
|
||||
void assert_fail_impl();
|
||||
[[noreturn]] void unreachable_impl();
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define YUZU_NO_INLINE __declspec(noinline)
|
||||
#else
|
||||
#define YUZU_NO_INLINE __attribute__((noinline))
|
||||
#endif
|
||||
static void
|
||||
assert_noinline_call(const Fn& fn) {
|
||||
fn();
|
||||
assert_handle_failure();
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
([&]() YUZU_NO_INLINE { \
|
||||
if (!(_a_)) [[unlikely]] { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
assert_fail_impl(); \
|
||||
} \
|
||||
while (0)
|
||||
}())
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
([&]() YUZU_NO_INLINE { \
|
||||
if (!(_a_)) [[unlikely]] { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
|
||||
assert_fail_impl(); \
|
||||
} \
|
||||
while (0)
|
||||
}())
|
||||
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
LOG_CRITICAL(Debug, "Unreachable code!"); \
|
||||
unreachable_impl(); \
|
||||
} while (0)
|
||||
|
||||
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
|
||||
#define UNREACHABLE_MSG(...) \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
|
||||
do { \
|
||||
LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
|
||||
unreachable_impl(); \
|
||||
} while (0)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
|
||||
167
src/common/bounded_threadsafe_queue.h
Normal file
167
src/common/bounded_threadsafe_queue.h
Normal file
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <stop_token>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(__cpp_lib_hardware_interference_size)
|
||||
constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
|
||||
#else
|
||||
constexpr size_t hardware_interference_size = 64;
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
#endif
|
||||
|
||||
template <typename T, size_t capacity = 0x400>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
|
||||
// Allocate one extra slot to prevent false sharing on the last slot
|
||||
slots = allocator.allocate(capacity + 1);
|
||||
// Allocators are not required to honor alignment for over-aligned types
|
||||
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
|
||||
// alignment here
|
||||
if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::construct_at(&slots[i]);
|
||||
}
|
||||
static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
|
||||
static_assert(alignof(Slot<T>) == hardware_interference_size,
|
||||
"Slot must be aligned to cache line boundary to prevent false sharing");
|
||||
static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
|
||||
"Slot size must be a multiple of cache line size to prevent "
|
||||
"false sharing between adjacent slots");
|
||||
static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
|
||||
"Queue size must be a multiple of cache line size to "
|
||||
"prevent false sharing between adjacent queues");
|
||||
}
|
||||
|
||||
~MPSCQueue() noexcept {
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::destroy_at(&slots[i]);
|
||||
}
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
}
|
||||
|
||||
// The queue must be both non-copyable and non-movable
|
||||
MPSCQueue(const MPSCQueue&) = delete;
|
||||
MPSCQueue& operator=(const MPSCQueue&) = delete;
|
||||
|
||||
MPSCQueue(MPSCQueue&&) = delete;
|
||||
MPSCQueue& operator=(MPSCQueue&&) = delete;
|
||||
|
||||
void Push(const T& v) noexcept {
|
||||
static_assert(std::is_nothrow_copy_constructible_v<T>,
|
||||
"T must be nothrow copy constructible");
|
||||
emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
|
||||
void Push(P&& v) noexcept {
|
||||
emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
void Pop(T& v, std::stop_token stop) noexcept {
|
||||
auto const tail = tail_.fetch_add(1);
|
||||
auto& slot = slots[idx(tail)];
|
||||
if (!slot.turn.test()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
|
||||
}
|
||||
v = slot.move();
|
||||
slot.destroy();
|
||||
slot.turn.clear();
|
||||
slot.turn.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U = T>
|
||||
struct Slot {
|
||||
~Slot() noexcept {
|
||||
if (turn.test()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy() noexcept {
|
||||
static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
|
||||
std::destroy_at(reinterpret_cast<U*>(&storage));
|
||||
}
|
||||
|
||||
U&& move() noexcept {
|
||||
return reinterpret_cast<U&&>(storage);
|
||||
}
|
||||
|
||||
// Align to avoid false sharing between adjacent slots
|
||||
alignas(hardware_interference_size) std::atomic_flag turn{};
|
||||
struct aligned_store {
|
||||
struct type {
|
||||
alignas(U) unsigned char data[sizeof(U)];
|
||||
};
|
||||
};
|
||||
typename aligned_store::type storage;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
auto const head = head_.fetch_add(1);
|
||||
auto& slot = slots[idx(head)];
|
||||
slot.turn.wait(true);
|
||||
slot.construct(std::forward<Args>(args)...);
|
||||
slot.turn.test_and_set();
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
constexpr size_t idx(size_t i) const noexcept {
|
||||
return i & mask;
|
||||
}
|
||||
|
||||
static constexpr size_t mask = capacity - 1;
|
||||
|
||||
// Align to avoid false sharing between head_ and tail_
|
||||
alignas(hardware_interference_size) std::atomic<size_t> head_{0};
|
||||
alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
|
||||
|
||||
std::mutex cv_mutex;
|
||||
std::condition_variable_any cv;
|
||||
|
||||
Slot<T>* slots;
|
||||
[[no_unique_address]] std::allocator<Slot<T>> allocator;
|
||||
|
||||
static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
|
||||
"T must be nothrow copy or move assignable");
|
||||
|
||||
static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
@@ -33,9 +33,9 @@ void DetachedTasks::AddTask(std::function<void()> task) {
|
||||
++instance->count;
|
||||
std::thread([task{std::move(task)}]() {
|
||||
task();
|
||||
std::unique_lock lock{instance->mutex};
|
||||
std::unique_lock thread_lock{instance->mutex};
|
||||
--instance->count;
|
||||
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
|
||||
std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
333
src/common/elf.h
Normal file
333
src/common/elf.h
Normal file
@@ -0,0 +1,333 @@
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Common {
|
||||
namespace ELF {
|
||||
|
||||
/* Type for a 16-bit quantity. */
|
||||
using Elf32_Half = u16;
|
||||
using Elf64_Half = u16;
|
||||
|
||||
/* Types for signed and unsigned 32-bit quantities. */
|
||||
using Elf32_Word = u32;
|
||||
using Elf32_Sword = s32;
|
||||
using Elf64_Word = u32;
|
||||
using Elf64_Sword = s32;
|
||||
|
||||
/* Types for signed and unsigned 64-bit quantities. */
|
||||
using Elf32_Xword = u64;
|
||||
using Elf32_Sxword = s64;
|
||||
using Elf64_Xword = u64;
|
||||
using Elf64_Sxword = s64;
|
||||
|
||||
/* Type of addresses. */
|
||||
using Elf32_Addr = u32;
|
||||
using Elf64_Addr = u64;
|
||||
|
||||
/* Type of file offsets. */
|
||||
using Elf32_Off = u32;
|
||||
using Elf64_Off = u64;
|
||||
|
||||
/* Type for section indices, which are 16-bit quantities. */
|
||||
using Elf32_Section = u16;
|
||||
using Elf64_Section = u16;
|
||||
|
||||
/* Type for version symbol information. */
|
||||
using Elf32_Versym = Elf32_Half;
|
||||
using Elf64_Versym = Elf64_Half;
|
||||
|
||||
constexpr size_t ElfIdentSize = 16;
|
||||
|
||||
/* The ELF file header. This appears at the start of every ELF file. */
|
||||
|
||||
struct Elf32_Ehdr {
|
||||
std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
|
||||
Elf32_Half e_type; /* Object file type */
|
||||
Elf32_Half e_machine; /* Architecture */
|
||||
Elf32_Word e_version; /* Object file version */
|
||||
Elf32_Addr e_entry; /* Entry point virtual address */
|
||||
Elf32_Off e_phoff; /* Program header table file offset */
|
||||
Elf32_Off e_shoff; /* Section header table file offset */
|
||||
Elf32_Word e_flags; /* Processor-specific flags */
|
||||
Elf32_Half e_ehsize; /* ELF header size in bytes */
|
||||
Elf32_Half e_phentsize; /* Program header table entry size */
|
||||
Elf32_Half e_phnum; /* Program header table entry count */
|
||||
Elf32_Half e_shentsize; /* Section header table entry size */
|
||||
Elf32_Half e_shnum; /* Section header table entry count */
|
||||
Elf32_Half e_shstrndx; /* Section header string table index */
|
||||
};
|
||||
|
||||
struct Elf64_Ehdr {
|
||||
std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
|
||||
Elf64_Half e_type; /* Object file type */
|
||||
Elf64_Half e_machine; /* Architecture */
|
||||
Elf64_Word e_version; /* Object file version */
|
||||
Elf64_Addr e_entry; /* Entry point virtual address */
|
||||
Elf64_Off e_phoff; /* Program header table file offset */
|
||||
Elf64_Off e_shoff; /* Section header table file offset */
|
||||
Elf64_Word e_flags; /* Processor-specific flags */
|
||||
Elf64_Half e_ehsize; /* ELF header size in bytes */
|
||||
Elf64_Half e_phentsize; /* Program header table entry size */
|
||||
Elf64_Half e_phnum; /* Program header table entry count */
|
||||
Elf64_Half e_shentsize; /* Section header table entry size */
|
||||
Elf64_Half e_shnum; /* Section header table entry count */
|
||||
Elf64_Half e_shstrndx; /* Section header string table index */
|
||||
};
|
||||
|
||||
constexpr u8 ElfClass32 = 1; /* 32-bit objects */
|
||||
constexpr u8 ElfClass64 = 2; /* 64-bit objects */
|
||||
constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
|
||||
constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
|
||||
constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
|
||||
|
||||
constexpr u16 ElfTypeNone = 0; /* No file type */
|
||||
constexpr u16 ElfTypeRel = 0; /* Relocatable file */
|
||||
constexpr u16 ElfTypeExec = 0; /* Executable file */
|
||||
constexpr u16 ElfTypeDyn = 0; /* Shared object file */
|
||||
|
||||
constexpr u16 ElfMachineArm = 40; /* ARM */
|
||||
constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
|
||||
|
||||
constexpr std::array<u8, ElfIdentSize> Elf32Ident{
|
||||
0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
|
||||
|
||||
constexpr std::array<u8, ElfIdentSize> Elf64Ident{
|
||||
0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
|
||||
|
||||
/* Section header. */
|
||||
|
||||
struct Elf32_Shdr {
|
||||
Elf32_Word sh_name; /* Section name (string tbl index) */
|
||||
Elf32_Word sh_type; /* Section type */
|
||||
Elf32_Word sh_flags; /* Section flags */
|
||||
Elf32_Addr sh_addr; /* Section virtual addr at execution */
|
||||
Elf32_Off sh_offset; /* Section file offset */
|
||||
Elf32_Word sh_size; /* Section size in bytes */
|
||||
Elf32_Word sh_link; /* Link to another section */
|
||||
Elf32_Word sh_info; /* Additional section information */
|
||||
Elf32_Word sh_addralign; /* Section alignment */
|
||||
Elf32_Word sh_entsize; /* Entry size if section holds table */
|
||||
};
|
||||
|
||||
struct Elf64_Shdr {
|
||||
Elf64_Word sh_name; /* Section name (string tbl index) */
|
||||
Elf64_Word sh_type; /* Section type */
|
||||
Elf64_Xword sh_flags; /* Section flags */
|
||||
Elf64_Addr sh_addr; /* Section virtual addr at execution */
|
||||
Elf64_Off sh_offset; /* Section file offset */
|
||||
Elf64_Xword sh_size; /* Section size in bytes */
|
||||
Elf64_Word sh_link; /* Link to another section */
|
||||
Elf64_Word sh_info; /* Additional section information */
|
||||
Elf64_Xword sh_addralign; /* Section alignment */
|
||||
Elf64_Xword sh_entsize; /* Entry size if section holds table */
|
||||
};
|
||||
|
||||
constexpr u32 ElfShnUndef = 0; /* Undefined section */
|
||||
|
||||
constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
|
||||
constexpr u32 ElfShtProgBits = 1; /* Program data */
|
||||
constexpr u32 ElfShtSymtab = 2; /* Symbol table */
|
||||
constexpr u32 ElfShtStrtab = 3; /* String table */
|
||||
constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
|
||||
constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
|
||||
constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
|
||||
constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
|
||||
constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
|
||||
|
||||
/* Symbol table entry. */
|
||||
|
||||
struct Elf32_Sym {
|
||||
Elf32_Word st_name; /* Symbol name (string tbl index) */
|
||||
Elf32_Addr st_value; /* Symbol value */
|
||||
Elf32_Word st_size; /* Symbol size */
|
||||
u8 st_info; /* Symbol type and binding */
|
||||
u8 st_other; /* Symbol visibility */
|
||||
Elf32_Section st_shndx; /* Section index */
|
||||
};
|
||||
|
||||
struct Elf64_Sym {
|
||||
Elf64_Word st_name; /* Symbol name (string tbl index) */
|
||||
u8 st_info; /* Symbol type and binding */
|
||||
u8 st_other; /* Symbol visibility */
|
||||
Elf64_Section st_shndx; /* Section index */
|
||||
Elf64_Addr st_value; /* Symbol value */
|
||||
Elf64_Xword st_size; /* Symbol size */
|
||||
};
|
||||
|
||||
/* How to extract and insert information held in the st_info field. */
|
||||
|
||||
static inline u8 ElfStBind(u8 st_info) {
|
||||
return st_info >> 4;
|
||||
}
|
||||
static inline u8 ElfStType(u8 st_info) {
|
||||
return st_info & 0xf;
|
||||
}
|
||||
static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
|
||||
return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
|
||||
}
|
||||
|
||||
constexpr u8 ElfBindLocal = 0; /* Local symbol */
|
||||
constexpr u8 ElfBindGlobal = 1; /* Global symbol */
|
||||
constexpr u8 ElfBindWeak = 2; /* Weak symbol */
|
||||
|
||||
constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
|
||||
constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
|
||||
constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
|
||||
|
||||
static inline u8 ElfStVisibility(u8 st_other) {
|
||||
return static_cast<u8>(st_other & 0x3);
|
||||
}
|
||||
|
||||
constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
|
||||
constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
|
||||
constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
|
||||
constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
|
||||
|
||||
/* Relocation table entry without addend (in section of type ShtRel). */
|
||||
|
||||
struct Elf32_Rel {
|
||||
Elf32_Addr r_offset; /* Address */
|
||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||
};
|
||||
|
||||
/* Relocation table entry with addend (in section of type ShtRela). */
|
||||
|
||||
struct Elf32_Rela {
|
||||
Elf32_Addr r_offset; /* Address */
|
||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||
Elf32_Sword r_addend; /* Addend */
|
||||
};
|
||||
|
||||
struct Elf64_Rela {
|
||||
Elf64_Addr r_offset; /* Address */
|
||||
Elf64_Xword r_info; /* Relocation type and symbol index */
|
||||
Elf64_Sxword r_addend; /* Addend */
|
||||
};
|
||||
|
||||
/* How to extract and insert information held in the r_info field. */
|
||||
|
||||
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
|
||||
return r_info >> 8;
|
||||
}
|
||||
static inline u8 Elf32RelType(Elf32_Word r_info) {
|
||||
return static_cast<u8>(r_info & 0xff);
|
||||
}
|
||||
static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
|
||||
return (sym_index << 8) + type;
|
||||
}
|
||||
static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
|
||||
return static_cast<u32>(r_info >> 32);
|
||||
}
|
||||
static inline u32 Elf64RelType(Elf64_Xword r_info) {
|
||||
return r_info & 0xffffffff;
|
||||
}
|
||||
static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
|
||||
return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
|
||||
}
|
||||
|
||||
constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
|
||||
constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
|
||||
constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
|
||||
constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
|
||||
|
||||
constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
|
||||
constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
|
||||
constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
|
||||
constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
|
||||
|
||||
/* Program segment header. */
|
||||
|
||||
struct Elf32_Phdr {
|
||||
Elf32_Word p_type; /* Segment type */
|
||||
Elf32_Off p_offset; /* Segment file offset */
|
||||
Elf32_Addr p_vaddr; /* Segment virtual address */
|
||||
Elf32_Addr p_paddr; /* Segment physical address */
|
||||
Elf32_Word p_filesz; /* Segment size in file */
|
||||
Elf32_Word p_memsz; /* Segment size in memory */
|
||||
Elf32_Word p_flags; /* Segment flags */
|
||||
Elf32_Word p_align; /* Segment alignment */
|
||||
};
|
||||
|
||||
struct Elf64_Phdr {
|
||||
Elf64_Word p_type; /* Segment type */
|
||||
Elf64_Word p_flags; /* Segment flags */
|
||||
Elf64_Off p_offset; /* Segment file offset */
|
||||
Elf64_Addr p_vaddr; /* Segment virtual address */
|
||||
Elf64_Addr p_paddr; /* Segment physical address */
|
||||
Elf64_Xword p_filesz; /* Segment size in file */
|
||||
Elf64_Xword p_memsz; /* Segment size in memory */
|
||||
Elf64_Xword p_align; /* Segment alignment */
|
||||
};
|
||||
|
||||
/* Legal values for p_type (segment type). */
|
||||
|
||||
constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
|
||||
constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
|
||||
constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
|
||||
constexpr u32 ElfPtInterp = 3; /* Program interpreter */
|
||||
constexpr u32 ElfPtNote = 4; /* Auxiliary information */
|
||||
constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
|
||||
constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
|
||||
|
||||
/* Legal values for p_flags (segment flags). */
|
||||
|
||||
constexpr u32 ElfPfExec = 0; /* Segment is executable */
|
||||
constexpr u32 ElfPfWrite = 1; /* Segment is writable */
|
||||
constexpr u32 ElfPfRead = 2; /* Segment is readable */
|
||||
|
||||
/* Dynamic section entry. */
|
||||
|
||||
struct Elf32_Dyn {
|
||||
Elf32_Sword d_tag; /* Dynamic entry type */
|
||||
union {
|
||||
Elf32_Word d_val; /* Integer value */
|
||||
Elf32_Addr d_ptr; /* Address value */
|
||||
} d_un;
|
||||
};
|
||||
|
||||
struct Elf64_Dyn {
|
||||
Elf64_Sxword d_tag; /* Dynamic entry type */
|
||||
union {
|
||||
Elf64_Xword d_val; /* Integer value */
|
||||
Elf64_Addr d_ptr; /* Address value */
|
||||
} d_un;
|
||||
};
|
||||
|
||||
/* Legal values for d_tag (dynamic entry type). */
|
||||
|
||||
constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
|
||||
constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
|
||||
constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
|
||||
constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
|
||||
constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
|
||||
constexpr u32 ElfDtStrtab = 5; /* Address of string table */
|
||||
constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
|
||||
constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
|
||||
constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
|
||||
constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
|
||||
constexpr u32 ElfDtStrsz = 10; /* Size of string table */
|
||||
constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
|
||||
constexpr u32 ElfDtInit = 12; /* Address of init function */
|
||||
constexpr u32 ElfDtFini = 13; /* Address of termination function */
|
||||
constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
|
||||
constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
|
||||
constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
|
||||
constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
|
||||
constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
|
||||
constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
|
||||
constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
|
||||
constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
|
||||
constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
|
||||
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
|
||||
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
||||
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
|
||||
|
||||
} // namespace ELF
|
||||
} // namespace Common
|
||||
@@ -20,10 +20,8 @@ struct Fiber::FiberImpl {
|
||||
VirtualBuffer<u8> rewind_stack;
|
||||
|
||||
std::mutex guard;
|
||||
std::function<void(void*)> entry_point;
|
||||
std::function<void(void*)> rewind_point;
|
||||
void* rewind_parameter{};
|
||||
void* start_parameter{};
|
||||
std::function<void()> entry_point;
|
||||
std::function<void()> rewind_point;
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
bool is_thread_fiber{};
|
||||
bool released{};
|
||||
@@ -34,13 +32,8 @@ struct Fiber::FiberImpl {
|
||||
boost::context::detail::fcontext_t rewind_context{};
|
||||
};
|
||||
|
||||
void Fiber::SetStartParameter(void* new_parameter) {
|
||||
impl->start_parameter = new_parameter;
|
||||
}
|
||||
|
||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
|
||||
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
|
||||
impl->rewind_point = std::move(rewind_func);
|
||||
impl->rewind_parameter = rewind_param;
|
||||
}
|
||||
|
||||
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||
@@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||
impl->previous_fiber->impl->context = transfer.fctx;
|
||||
impl->previous_fiber->impl->guard.unlock();
|
||||
impl->previous_fiber.reset();
|
||||
impl->entry_point(impl->start_parameter);
|
||||
impl->entry_point();
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
|
||||
u8* tmp = impl->stack_limit;
|
||||
impl->stack_limit = impl->rewind_stack_limit;
|
||||
impl->rewind_stack_limit = tmp;
|
||||
impl->rewind_point(impl->rewind_parameter);
|
||||
impl->rewind_point();
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
|
||||
fiber->OnRewind(transfer);
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
||||
: impl{std::make_unique<FiberImpl>()} {
|
||||
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->start_parameter = start_parameter;
|
||||
impl->stack_limit = impl->stack.data();
|
||||
impl->rewind_stack_limit = impl->rewind_stack.data();
|
||||
u8* stack_base = impl->stack_limit + default_stack_size;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Common {
|
||||
*/
|
||||
class Fiber {
|
||||
public:
|
||||
Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter);
|
||||
Fiber(std::function<void()>&& entry_point_func);
|
||||
~Fiber();
|
||||
|
||||
Fiber(const Fiber&) = delete;
|
||||
@@ -43,16 +43,13 @@ public:
|
||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
|
||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
|
||||
void SetRewindPoint(std::function<void()>&& rewind_func);
|
||||
|
||||
void Rewind();
|
||||
|
||||
/// Only call from main thread's fiber
|
||||
void Exit();
|
||||
|
||||
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
||||
void SetStartParameter(void* new_parameter);
|
||||
|
||||
private:
|
||||
Fiber();
|
||||
|
||||
|
||||
@@ -232,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
|
||||
fs::path GetExeDirectory() {
|
||||
wchar_t exe_path[MAX_PATH];
|
||||
|
||||
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
|
||||
|
||||
if (!exe_path) {
|
||||
if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to get the path to the executable of the current process");
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ enum class PollingError {
|
||||
enum class VibrationAmplificationType {
|
||||
Linear,
|
||||
Exponential,
|
||||
Test,
|
||||
};
|
||||
|
||||
// Analog properties for calibration
|
||||
|
||||
@@ -15,6 +15,9 @@ enum class PageType : u8 {
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||
/// the callbacks.
|
||||
DebugMemory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
|
||||
@@ -76,7 +76,7 @@ std::string ParamPackage::Serialize() const {
|
||||
std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
|
||||
auto pair = data.find(key);
|
||||
if (pair == data.end()) {
|
||||
LOG_DEBUG(Common, "key '{}' not found", key);
|
||||
LOG_TRACE(Common, "key '{}' not found", key);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
|
||||
int ParamPackage::Get(const std::string& key, int default_value) const {
|
||||
auto pair = data.find(key);
|
||||
if (pair == data.end()) {
|
||||
LOG_DEBUG(Common, "key '{}' not found", key);
|
||||
LOG_TRACE(Common, "key '{}' not found", key);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
|
||||
float ParamPackage::Get(const std::string& key, float default_value) const {
|
||||
auto pair = data.find(key);
|
||||
if (pair == data.end()) {
|
||||
LOG_DEBUG(Common, "key {} not found", key);
|
||||
LOG_TRACE(Common, "key {} not found", key);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ void LogSettings() {
|
||||
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
|
||||
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
|
||||
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
|
||||
log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
|
||||
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
|
||||
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
|
||||
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
|
||||
@@ -146,7 +147,7 @@ void UpdateRescalingInfo() {
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
ASSERT(false);
|
||||
info.up_scale = 1;
|
||||
info.down_shift = 0;
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ struct Values {
|
||||
|
||||
// Renderer
|
||||
RangedSetting<RendererBackend> renderer_backend{
|
||||
RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||
BasicSetting<bool> renderer_debug{false, "debug"};
|
||||
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
|
||||
@@ -601,7 +601,7 @@ struct Values {
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
|
||||
BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
|
||||
BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
|
||||
@@ -178,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
|
||||
|
||||
#endif
|
||||
|
||||
std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
|
||||
return std::u16string(reinterpret_cast<const char16_t*>(input), length);
|
||||
}
|
||||
|
||||
std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
|
||||
|
||||
@@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||
|
||||
/**
|
||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||
* `other` for equality.
|
||||
|
||||
@@ -47,6 +47,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
case ThreadPriority::VeryHigh:
|
||||
windows_priority = THREAD_PRIORITY_HIGHEST;
|
||||
break;
|
||||
case ThreadPriority::Critical:
|
||||
windows_priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
break;
|
||||
default:
|
||||
windows_priority = THREAD_PRIORITY_NORMAL;
|
||||
break;
|
||||
@@ -59,9 +62,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
pthread_t this_thread = pthread_self();
|
||||
|
||||
s32 max_prio = sched_get_priority_max(SCHED_OTHER);
|
||||
s32 min_prio = sched_get_priority_min(SCHED_OTHER);
|
||||
u32 level = static_cast<u32>(new_priority) + 1;
|
||||
const auto scheduling_type = SCHED_OTHER;
|
||||
s32 max_prio = sched_get_priority_max(scheduling_type);
|
||||
s32 min_prio = sched_get_priority_min(scheduling_type);
|
||||
u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U);
|
||||
|
||||
struct sched_param params;
|
||||
if (max_prio > min_prio) {
|
||||
@@ -70,7 +74,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
|
||||
}
|
||||
|
||||
pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms);
|
||||
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -92,6 +92,7 @@ enum class ThreadPriority : u32 {
|
||||
Normal = 1,
|
||||
High = 2,
|
||||
VeryHigh = 3,
|
||||
Critical = 4,
|
||||
};
|
||||
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority);
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace Common {
|
||||
#else
|
||||
return _udiv128(r[1], r[0], d, &remainder);
|
||||
#endif
|
||||
#else
|
||||
#ifdef __SIZEOF_INT128__
|
||||
const auto product = static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b);
|
||||
return static_cast<u64>(product / d);
|
||||
#else
|
||||
const u64 diva = a / d;
|
||||
const u64 moda = a % d;
|
||||
@@ -37,6 +41,7 @@ namespace Common {
|
||||
const u64 modb = b % d;
|
||||
return diva * b + moda * divb + moda * modb / d;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function multiplies 2 u64 values and produces a u128 value;
|
||||
|
||||
@@ -75,8 +75,8 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
|
||||
}
|
||||
|
||||
u64 NativeClock::GetRTSC() {
|
||||
TimePoint new_time_point{};
|
||||
TimePoint current_time_point{};
|
||||
TimePoint new_time_point{};
|
||||
|
||||
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
|
||||
do {
|
||||
@@ -89,8 +89,7 @@ u64 NativeClock::GetRTSC() {
|
||||
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
|
||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
||||
current_time_point.pack, current_time_point.pack));
|
||||
/// The clock cannot be more precise than the guest timer, remove the lower bits
|
||||
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
|
||||
return new_time_point.inner.accumulated_ticks;
|
||||
}
|
||||
|
||||
void NativeClock::Pause(bool is_paused) {
|
||||
|
||||
@@ -37,12 +37,8 @@ private:
|
||||
} inner;
|
||||
};
|
||||
|
||||
/// value used to reduce the native clocks accuracy as some apss rely on
|
||||
/// undefined behavior where the level of accuracy in the clock shouldn't
|
||||
/// be higher.
|
||||
static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
|
||||
|
||||
TimePoint time_point;
|
||||
|
||||
// factors
|
||||
u64 clock_rtsc_factor{};
|
||||
u64 cpu_rtsc_factor{};
|
||||
|
||||
@@ -36,6 +36,13 @@ add_library(core STATIC
|
||||
crypto/ctr_encryption_layer.h
|
||||
crypto/xts_encryption_layer.cpp
|
||||
crypto/xts_encryption_layer.h
|
||||
debugger/debugger_interface.h
|
||||
debugger/debugger.cpp
|
||||
debugger/debugger.h
|
||||
debugger/gdbstub_arch.cpp
|
||||
debugger/gdbstub_arch.h
|
||||
debugger/gdbstub.cpp
|
||||
debugger/gdbstub.h
|
||||
device_memory.cpp
|
||||
device_memory.h
|
||||
file_sys/bis_factory.cpp
|
||||
@@ -215,7 +222,7 @@ add_library(core STATIC
|
||||
hle/kernel/k_page_buffer.h
|
||||
hle/kernel/k_page_heap.cpp
|
||||
hle/kernel/k_page_heap.h
|
||||
hle/kernel/k_page_linked_list.h
|
||||
hle/kernel/k_page_group.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
hle/kernel/k_port.cpp
|
||||
@@ -736,16 +743,11 @@ if (MSVC)
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
)
|
||||
else()
|
||||
target_compile_options(core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=shadow
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
@@ -761,6 +763,9 @@ create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
|
||||
if (MINGW)
|
||||
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -88,4 +90,96 @@ void ARM_Interface::LogBacktrace() const {
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Interface::Run() {
|
||||
using Kernel::StepState;
|
||||
using Kernel::SuspendType;
|
||||
|
||||
while (true) {
|
||||
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
|
||||
Dynarmic::HaltReason hr{};
|
||||
|
||||
// Notify the debugger and go to sleep if a step was performed
|
||||
// and this thread has been scheduled again.
|
||||
if (current_thread->GetStepState() == StepState::StepPerformed) {
|
||||
system.GetDebugger().NotifyThreadStopped(current_thread);
|
||||
current_thread->RequestSuspend(SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, run the thread.
|
||||
system.EnterDynarmicProfile();
|
||||
if (current_thread->GetStepState() == StepState::StepPending) {
|
||||
hr = StepJit();
|
||||
|
||||
if (Has(hr, step_thread)) {
|
||||
current_thread->SetStepState(StepState::StepPerformed);
|
||||
}
|
||||
} else {
|
||||
hr = RunJit();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (Has(hr, breakpoint) || Has(hr, no_execute)) {
|
||||
RewindBreakpointInstruction();
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.GetDebugger().NotifyThreadStopped(current_thread);
|
||||
}
|
||||
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
// Notify the debugger and go to sleep if a watchpoint was hit.
|
||||
if (Has(hr, watchpoint)) {
|
||||
RewindBreakpointInstruction();
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
||||
}
|
||||
current_thread->RequestSuspend(SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle syscalls and scheduling (this may change the current thread)
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, GetSvcNumber());
|
||||
}
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
|
||||
watchpoints = ℘
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint(
|
||||
VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const {
|
||||
if (!watchpoints) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const VAddr start_address{addr};
|
||||
const VAddr end_address{addr + size};
|
||||
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) {
|
||||
const auto& watch{(*watchpoints)[i]};
|
||||
|
||||
if (end_address <= watch.start_address) {
|
||||
continue;
|
||||
}
|
||||
if (start_address >= watch.end_address) {
|
||||
continue;
|
||||
}
|
||||
if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return &watch;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <dynarmic/interface/halt_reason.h>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -16,13 +20,16 @@ struct PageTable;
|
||||
|
||||
namespace Kernel {
|
||||
enum class VMAPermission : u8;
|
||||
}
|
||||
enum class DebugWatchpointType : u8;
|
||||
struct DebugWatchpoint;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
class CPUInterruptHandler;
|
||||
|
||||
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
|
||||
using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
|
||||
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface {
|
||||
@@ -64,10 +71,7 @@ public:
|
||||
static_assert(sizeof(ThreadContext64) == 0x320);
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
|
||||
/// Step CPU by one instruction
|
||||
virtual void Step() = 0;
|
||||
void Run();
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
@@ -170,6 +174,7 @@ public:
|
||||
virtual void SaveContext(ThreadContext64& ctx) = 0;
|
||||
virtual void LoadContext(const ThreadContext32& ctx) = 0;
|
||||
virtual void LoadContext(const ThreadContext64& ctx) = 0;
|
||||
void LoadWatchpointArray(const WatchpointArray& wp);
|
||||
|
||||
/// Clears the exclusive monitor's state.
|
||||
virtual void ClearExclusiveState() = 0;
|
||||
@@ -194,13 +199,29 @@ public:
|
||||
|
||||
void LogBacktrace() const;
|
||||
|
||||
static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
|
||||
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
|
||||
static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
|
||||
|
||||
protected:
|
||||
/// System context that this ARM interface is running under.
|
||||
System& system;
|
||||
CPUInterrupts& interrupt_handlers;
|
||||
const WatchpointArray* watchpoints;
|
||||
bool uses_wall_clock;
|
||||
|
||||
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
|
||||
const Kernel::DebugWatchpoint* MatchingWatchpoint(
|
||||
VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const;
|
||||
|
||||
virtual Dynarmic::HaltReason RunJit() = 0;
|
||||
virtual Dynarmic::HaltReason StepJit() = 0;
|
||||
virtual u32 GetSvcNumber() const = 0;
|
||||
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
|
||||
virtual void RewindBreakpointInstruction() = 0;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -24,70 +26,102 @@ namespace Core {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
|
||||
: parent{parent_}, memory(parent.system.Memory()) {}
|
||||
: parent{parent_},
|
||||
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
|
||||
|
||||
u8 MemoryRead8(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read64(vaddr);
|
||||
}
|
||||
std::optional<u32> MemoryReadCode(u32 vaddr) override {
|
||||
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return MemoryRead32(vaddr);
|
||||
}
|
||||
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override {
|
||||
memory.Write8(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override {
|
||||
memory.Write16(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override {
|
||||
memory.Write32(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override {
|
||||
memory.Write64(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||
parent.LogBacktrace();
|
||||
UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
|
||||
MemoryReadCode(pc));
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryRead32(pc));
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
parent.LogBacktrace();
|
||||
LOG_CRITICAL(Core_ARM,
|
||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
|
||||
UNIMPLEMENTED();
|
||||
switch (exception) {
|
||||
case Dynarmic::A32::Exception::NoExecuteFault:
|
||||
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
|
||||
ReturnException(pc, ARM_Interface::no_execute);
|
||||
return;
|
||||
default:
|
||||
if (debugger_enabled) {
|
||||
ReturnException(pc, ARM_Interface::breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
parent.LogBacktrace();
|
||||
LOG_CRITICAL(Core_ARM,
|
||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||
exception, pc, MemoryRead32(pc), parent.IsInThumbMode());
|
||||
}
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
parent.svc_swi = swi;
|
||||
parent.jit.load()->HaltExecution(svc_call);
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -113,9 +147,31 @@ public:
|
||||
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
|
||||
}
|
||||
|
||||
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
|
||||
if (!debugger_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||
if (match) {
|
||||
parent.halted_watchpoint = match;
|
||||
ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReturnException(u32 pc, Dynarmic::HaltReason hr) {
|
||||
parent.SaveContext(parent.breakpoint_context);
|
||||
parent.breakpoint_context.cpu_registers[15] = pc;
|
||||
parent.jit.load()->HaltExecution(hr);
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32& parent;
|
||||
Core::Memory::Memory& memory;
|
||||
std::size_t num_interpreted_instructions{};
|
||||
bool debugger_enabled{};
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
|
||||
@@ -150,6 +206,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
config.code_cache_size = 512_MiB;
|
||||
config.far_code_offset = 400_MiB;
|
||||
|
||||
// Allow memory fault handling to work
|
||||
if (system.DebuggerEnabled()) {
|
||||
config.check_halt_on_memory_access = true;
|
||||
}
|
||||
|
||||
// null_jit
|
||||
if (!page_table) {
|
||||
// Don't waste too much memory on null_jit
|
||||
@@ -232,20 +293,24 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::Run() {
|
||||
while (true) {
|
||||
const auto hr = jit.load()->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
|
||||
return jit.load()->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::Step() {
|
||||
jit.load()->Step();
|
||||
Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
|
||||
return jit.load()->Step();
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic_32::GetSvcNumber() const {
|
||||
return svc_swi;
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
|
||||
return halted_watchpoint;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::RewindBreakpointInstruction() {
|
||||
LoadContext(breakpoint_context);
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
|
||||
@@ -41,8 +41,6 @@ public:
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -70,6 +68,13 @@ public:
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktrace() const override;
|
||||
|
||||
protected:
|
||||
Dynarmic::HaltReason RunJit() override;
|
||||
Dynarmic::HaltReason StepJit() override;
|
||||
u32 GetSvcNumber() const override;
|
||||
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
|
||||
void RewindBreakpointInstruction() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
|
||||
|
||||
@@ -95,6 +100,10 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
|
||||
// Watchpoint info
|
||||
const Kernel::DebugWatchpoint* halted_watchpoint;
|
||||
ThreadContext32 breakpoint_context;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -25,68 +26,92 @@ namespace Core {
|
||||
using Vector = Dynarmic::A64::Vector;
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
|
||||
: parent{parent_}, memory(parent.system.Memory()) {}
|
||||
: parent{parent_},
|
||||
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read64(vaddr);
|
||||
}
|
||||
Vector MemoryRead128(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
||||
}
|
||||
std::optional<u32> MemoryReadCode(u64 vaddr) override {
|
||||
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return MemoryRead32(vaddr);
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
memory.Write8(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
memory.Write16(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
memory.Write32(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
memory.Write64(vaddr, value);
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, Vector value) override {
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
|
||||
return memory.WriteExclusive128(vaddr, value, expected);
|
||||
return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive128(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
parent.LogBacktrace();
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
num_instructions, MemoryRead32(pc));
|
||||
}
|
||||
|
||||
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
|
||||
@@ -119,17 +144,25 @@ public:
|
||||
case Dynarmic::A64::Exception::SendEventLocal:
|
||||
case Dynarmic::A64::Exception::Yield:
|
||||
return;
|
||||
case Dynarmic::A64::Exception::Breakpoint:
|
||||
case Dynarmic::A64::Exception::NoExecuteFault:
|
||||
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
|
||||
ReturnException(pc, ARM_Interface::no_execute);
|
||||
return;
|
||||
default:
|
||||
if (debugger_enabled) {
|
||||
ReturnException(pc, ARM_Interface::breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
parent.LogBacktrace();
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
||||
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryRead32(pc));
|
||||
}
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
parent.svc_swi = swi;
|
||||
parent.jit.load()->HaltExecution(svc_call);
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -157,10 +190,32 @@ public:
|
||||
return parent.system.CoreTiming().GetClockTicks();
|
||||
}
|
||||
|
||||
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
|
||||
if (!debugger_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||
if (match) {
|
||||
parent.halted_watchpoint = match;
|
||||
ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReturnException(u64 pc, Dynarmic::HaltReason hr) {
|
||||
parent.SaveContext(parent.breakpoint_context);
|
||||
parent.breakpoint_context.pc = pc;
|
||||
parent.jit.load()->HaltExecution(hr);
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64& parent;
|
||||
Core::Memory::Memory& memory;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
bool debugger_enabled{};
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
|
||||
@@ -211,6 +266,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
config.code_cache_size = 512_MiB;
|
||||
config.far_code_offset = 400_MiB;
|
||||
|
||||
// Allow memory fault handling to work
|
||||
if (system.DebuggerEnabled()) {
|
||||
config.check_halt_on_memory_access = true;
|
||||
}
|
||||
|
||||
// null_jit
|
||||
if (!page_table) {
|
||||
// Don't waste too much memory on null_jit
|
||||
@@ -293,20 +353,24 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::Run() {
|
||||
while (true) {
|
||||
const auto hr = jit.load()->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
|
||||
return jit.load()->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::Step() {
|
||||
jit.load()->Step();
|
||||
Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
|
||||
return jit.load()->Step();
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic_64::GetSvcNumber() const {
|
||||
return svc_swi;
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
|
||||
return halted_watchpoint;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::RewindBreakpointInstruction() {
|
||||
LoadContext(breakpoint_context);
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
|
||||
@@ -39,8 +39,6 @@ public:
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -64,6 +62,13 @@ public:
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktrace() const override;
|
||||
|
||||
protected:
|
||||
Dynarmic::HaltReason RunJit() override;
|
||||
Dynarmic::HaltReason StepJit() override;
|
||||
u32 GetSvcNumber() const override;
|
||||
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
|
||||
void RewindBreakpointInstruction() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
|
||||
std::size_t address_space_bits) const;
|
||||
@@ -88,6 +93,10 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
|
||||
// Breakpoint info
|
||||
const Kernel::DebugWatchpoint* halted_watchpoint;
|
||||
ThreadContext64 breakpoint_context;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -20,7 +20,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> {
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
|
||||
return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,73 +3,14 @@
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/elf.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
using namespace Common::ELF;
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
|
||||
|
||||
enum class ELFSymbolType : u8 {
|
||||
None = 0,
|
||||
Object = 1,
|
||||
Function = 2,
|
||||
Section = 3,
|
||||
File = 4,
|
||||
Common = 5,
|
||||
TLS = 6,
|
||||
};
|
||||
|
||||
enum class ELFSymbolBinding : u8 {
|
||||
Local = 0,
|
||||
Global = 1,
|
||||
Weak = 2,
|
||||
};
|
||||
|
||||
enum class ELFSymbolVisibility : u8 {
|
||||
Default = 0,
|
||||
Internal = 1,
|
||||
Hidden = 2,
|
||||
Protected = 3,
|
||||
};
|
||||
|
||||
struct ELF64Symbol {
|
||||
u32 name_index;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
u64 value;
|
||||
u64 size;
|
||||
};
|
||||
static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
|
||||
|
||||
struct ELF32Symbol {
|
||||
u32 name_index;
|
||||
u32 value;
|
||||
u32 size;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
};
|
||||
static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Symbols {
|
||||
|
||||
template <typename Word, typename ELFSymbol, typename ByteReader>
|
||||
@@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
|
||||
const Word value = ReadWord(dynamic_index + sizeof(Word));
|
||||
dynamic_index += 2 * sizeof(Word);
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
if (tag == ElfDtNull) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
|
||||
if (tag == ElfDtStrtab) {
|
||||
string_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
|
||||
} else if (tag == ElfDtSymtab) {
|
||||
symbol_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
|
||||
} else if (tag == ElfDtSyment) {
|
||||
symbol_entry_size = value;
|
||||
}
|
||||
}
|
||||
@@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
|
||||
ELFSymbol symbol{};
|
||||
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_offset + symbol.name_index;
|
||||
VAddr string_offset = string_table_offset + symbol.st_name;
|
||||
std::string name;
|
||||
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
symbol_index += symbol_entry_size;
|
||||
out[name] = std::make_pair(symbol.value, symbol.size);
|
||||
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
|
||||
}
|
||||
|
||||
return out;
|
||||
@@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
|
||||
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
|
||||
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
|
||||
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
|
||||
}};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
|
||||
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
|
||||
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
@@ -137,7 +138,6 @@ struct System::Impl {
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
cpu_manager.Pause(false);
|
||||
is_paused = false;
|
||||
|
||||
return status;
|
||||
@@ -149,28 +149,29 @@ struct System::Impl {
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
cpu_manager.Pause(true);
|
||||
is_paused = true;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> StallCPU() {
|
||||
std::unique_lock<std::mutex> StallProcesses() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
kernel.Suspend(true);
|
||||
core_timing.SyncPause(true);
|
||||
cpu_manager.Pause(true);
|
||||
return lk;
|
||||
}
|
||||
|
||||
void UnstallCPU() {
|
||||
void UnstallProcesses() {
|
||||
if (!is_paused) {
|
||||
core_timing.SyncPause(false);
|
||||
kernel.Suspend(false);
|
||||
cpu_manager.Pause(false);
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
debugger = std::make_unique<Debugger>(system, port);
|
||||
}
|
||||
|
||||
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
@@ -329,6 +330,9 @@ struct System::Impl {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
kernel.ShutdownCores();
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
@@ -436,6 +440,9 @@ struct System::Impl {
|
||||
/// Network instance
|
||||
Network::NetworkInstance network_instance;
|
||||
|
||||
/// Debugger
|
||||
std::unique_ptr<Core::Debugger> debugger;
|
||||
|
||||
SystemResultStatus status = SystemResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
@@ -472,10 +479,6 @@ SystemResultStatus System::Pause() {
|
||||
return impl->Pause();
|
||||
}
|
||||
|
||||
SystemResultStatus System::SingleStep() {
|
||||
return SystemResultStatus::Success;
|
||||
}
|
||||
|
||||
void System::InvalidateCpuInstructionCaches() {
|
||||
impl->kernel.InvalidateAllInstructionCaches();
|
||||
}
|
||||
@@ -488,12 +491,22 @@ void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> System::StallCPU() {
|
||||
return impl->StallCPU();
|
||||
void System::DetachDebugger() {
|
||||
if (impl->debugger) {
|
||||
impl->debugger->NotifyShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void System::UnstallCPU() {
|
||||
impl->UnstallCPU();
|
||||
std::unique_lock<std::mutex> System::StallProcesses() {
|
||||
return impl->StallProcesses();
|
||||
}
|
||||
|
||||
void System::UnstallProcesses() {
|
||||
impl->UnstallProcesses();
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
|
||||
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
@@ -809,6 +822,18 @@ bool System::IsMulticore() const {
|
||||
return impl->is_multicore;
|
||||
}
|
||||
|
||||
bool System::DebuggerEnabled() const {
|
||||
return Settings::values.use_gdbstub.GetValue();
|
||||
}
|
||||
|
||||
Core::Debugger& System::GetDebugger() {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
const Core::Debugger& System::GetDebugger() const {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
|
||||
impl->execute_program_callback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
class CpuManager;
|
||||
class Debugger;
|
||||
class DeviceMemory;
|
||||
class ExclusiveMonitor;
|
||||
class SpeedLimiter;
|
||||
@@ -147,12 +148,6 @@ public:
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Pause();
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus SingleStep();
|
||||
|
||||
/**
|
||||
* Invalidate the CPU instruction caches
|
||||
* This function should only be used by GDB Stub to support breakpoints, memory updates and
|
||||
@@ -165,8 +160,16 @@ public:
|
||||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
|
||||
std::unique_lock<std::mutex> StallCPU();
|
||||
void UnstallCPU();
|
||||
/// Forcibly detach the debugger if it is running.
|
||||
void DetachDebugger();
|
||||
|
||||
std::unique_lock<std::mutex> StallProcesses();
|
||||
void UnstallProcesses();
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
*/
|
||||
void InitializeDebugger();
|
||||
|
||||
/**
|
||||
* Load an executable application.
|
||||
@@ -354,6 +357,9 @@ public:
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
[[nodiscard]] Core::Debugger& GetDebugger();
|
||||
[[nodiscard]] const Core::Debugger& GetDebugger() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
[[nodiscard]] bool GetExitLock() const;
|
||||
|
||||
@@ -375,6 +381,9 @@ public:
|
||||
/// Tells if system is running on multicore.
|
||||
[[nodiscard]] bool IsMulticore() const;
|
||||
|
||||
/// Tells if the system debugger is enabled.
|
||||
[[nodiscard]] bool DebuggerEnabled() const;
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to re-launch the application
|
||||
/// using a specified program index.
|
||||
using ExecuteProgramCallback = std::function<void(std::size_t)>;
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -41,11 +43,11 @@ CoreTiming::CoreTiming()
|
||||
|
||||
CoreTiming::~CoreTiming() = default;
|
||||
|
||||
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||
constexpr char name[] = "yuzu:HostTiming";
|
||||
MicroProfileOnThreadCreate(name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
|
||||
void CoreTiming::ThreadEntry(CoreTiming& instance, size_t id) {
|
||||
const std::string name = "yuzu:HostTiming_" + std::to_string(id);
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
instance.on_thread_init();
|
||||
instance.ThreadLoop();
|
||||
MicroProfileOnThreadExit();
|
||||
@@ -59,68 +61,97 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
|
||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||
if (is_multicore) {
|
||||
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
|
||||
const auto hardware_concurrency = std::thread::hardware_concurrency();
|
||||
size_t id = 0;
|
||||
worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++);
|
||||
if (hardware_concurrency > 8) {
|
||||
worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::Shutdown() {
|
||||
paused = true;
|
||||
is_paused = true;
|
||||
shutting_down = true;
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
if (timer_thread) {
|
||||
timer_thread->join();
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
|
||||
event_cv.notify_all();
|
||||
wait_pause_cv.notify_all();
|
||||
for (auto& thread : worker_threads) {
|
||||
thread.join();
|
||||
}
|
||||
worker_threads.clear();
|
||||
ClearPendingEvents();
|
||||
timer_thread.reset();
|
||||
has_started = false;
|
||||
}
|
||||
|
||||
void CoreTiming::Pause(bool is_paused) {
|
||||
paused = is_paused;
|
||||
pause_event.Set();
|
||||
}
|
||||
|
||||
void CoreTiming::SyncPause(bool is_paused) {
|
||||
if (is_paused == paused && paused_set == paused) {
|
||||
void CoreTiming::Pause(bool is_paused_) {
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
if (is_paused_ == paused_state.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
Pause(is_paused);
|
||||
if (timer_thread) {
|
||||
if (!is_paused) {
|
||||
pause_event.Set();
|
||||
if (is_multicore) {
|
||||
is_paused = is_paused_;
|
||||
event_cv.notify_all();
|
||||
if (!is_paused_) {
|
||||
wait_pause_cv.notify_all();
|
||||
}
|
||||
}
|
||||
paused_state.store(is_paused_, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void CoreTiming::SyncPause(bool is_paused_) {
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
if (is_paused_ == paused_state.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_multicore) {
|
||||
is_paused = is_paused_;
|
||||
event_cv.notify_all();
|
||||
if (!is_paused_) {
|
||||
wait_pause_cv.notify_all();
|
||||
}
|
||||
}
|
||||
paused_state.store(is_paused_, std::memory_order_relaxed);
|
||||
if (is_multicore) {
|
||||
if (is_paused_) {
|
||||
wait_signal_cv.wait(main_lock, [this] { return pause_count == worker_threads.size(); });
|
||||
} else {
|
||||
wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
|
||||
}
|
||||
event.Set();
|
||||
while (paused_set != is_paused)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
bool CoreTiming::IsRunning() const {
|
||||
return !paused_set;
|
||||
return !paused_state.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool CoreTiming::HasPendingEvents() const {
|
||||
return !(wait_set && event_queue.empty());
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
return !event_queue.empty() || pending_events.load(std::memory_order_relaxed) != 0;
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data) {
|
||||
{
|
||||
std::scoped_lock scope{basic_lock};
|
||||
const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
|
||||
|
||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
|
||||
pending_events.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
|
||||
if (is_multicore) {
|
||||
event_cv.notify_one();
|
||||
}
|
||||
event.Set();
|
||||
}
|
||||
|
||||
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data) {
|
||||
std::scoped_lock scope{basic_lock};
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get() && e.user_data == user_data;
|
||||
});
|
||||
@@ -129,6 +160,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
if (itr != event_queue.end()) {
|
||||
event_queue.erase(itr, event_queue.end());
|
||||
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
pending_events.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,11 +200,12 @@ u64 CoreTiming::GetClockTicks() const {
|
||||
}
|
||||
|
||||
void CoreTiming::ClearPendingEvents() {
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
std::scoped_lock lock{basic_lock};
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get();
|
||||
@@ -186,21 +219,28 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
std::scoped_lock lock{advance_lock, basic_lock};
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
|
||||
Event evt = std::move(event_queue.front());
|
||||
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
event_queue.pop_back();
|
||||
basic_lock.unlock();
|
||||
|
||||
if (const auto event_type{evt.type.lock()}) {
|
||||
event_type->callback(
|
||||
evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)});
|
||||
sequence_mutex.lock();
|
||||
event_mutex.unlock();
|
||||
|
||||
event_type->guard.lock();
|
||||
sequence_mutex.unlock();
|
||||
const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time);
|
||||
event_type->callback(evt.user_data, std::chrono::nanoseconds{delay});
|
||||
event_type->guard.unlock();
|
||||
|
||||
event_mutex.lock();
|
||||
pending_events.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
basic_lock.lock();
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
}
|
||||
|
||||
@@ -213,26 +253,34 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
}
|
||||
|
||||
void CoreTiming::ThreadLoop() {
|
||||
const auto predicate = [this] { return !event_queue.empty() || is_paused; };
|
||||
has_started = true;
|
||||
while (!shutting_down) {
|
||||
while (!paused) {
|
||||
paused_set = false;
|
||||
while (!is_paused && !shutting_down) {
|
||||
const auto next_time = Advance();
|
||||
if (next_time) {
|
||||
if (*next_time > 0) {
|
||||
std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
|
||||
event.WaitFor(next_time_ns);
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
event_cv.wait_for(main_lock, next_time_ns, predicate);
|
||||
}
|
||||
} else {
|
||||
wait_set = true;
|
||||
event.Wait();
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
event_cv.wait(main_lock, predicate);
|
||||
}
|
||||
wait_set = false;
|
||||
}
|
||||
paused_set = true;
|
||||
clock->Pause(true);
|
||||
pause_event.Wait();
|
||||
clock->Pause(false);
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
pause_count++;
|
||||
if (pause_count == worker_threads.size()) {
|
||||
clock->Pause(true);
|
||||
wait_signal_cv.notify_all();
|
||||
}
|
||||
wait_pause_cv.wait(main_lock, [this] { return !is_paused || shutting_down; });
|
||||
pause_count--;
|
||||
if (pause_count == 0) {
|
||||
clock->Pause(false);
|
||||
wait_signal_cv.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -14,7 +15,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
@@ -32,6 +32,7 @@ struct EventType {
|
||||
TimedCallback callback;
|
||||
/// A pointer to the name of the event.
|
||||
const std::string name;
|
||||
mutable std::mutex guard;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -131,7 +132,7 @@ private:
|
||||
/// Clear all pending events. This should ONLY be done on exit.
|
||||
void ClearPendingEvents();
|
||||
|
||||
static void ThreadEntry(CoreTiming& instance);
|
||||
static void ThreadEntry(CoreTiming& instance, size_t id);
|
||||
void ThreadLoop();
|
||||
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
@@ -144,21 +145,25 @@ private:
|
||||
// accomodated by the standard adaptor class.
|
||||
std::vector<Event> event_queue;
|
||||
u64 event_fifo_id = 0;
|
||||
std::atomic<size_t> pending_events{};
|
||||
|
||||
std::shared_ptr<EventType> ev_lost;
|
||||
Common::Event event{};
|
||||
Common::Event pause_event{};
|
||||
std::mutex basic_lock;
|
||||
std::mutex advance_lock;
|
||||
std::unique_ptr<std::thread> timer_thread;
|
||||
std::atomic<bool> paused{};
|
||||
std::atomic<bool> paused_set{};
|
||||
std::atomic<bool> wait_set{};
|
||||
std::atomic<bool> shutting_down{};
|
||||
std::atomic<bool> has_started{};
|
||||
std::function<void()> on_thread_init{};
|
||||
|
||||
std::vector<std::thread> worker_threads;
|
||||
|
||||
std::condition_variable event_cv;
|
||||
std::condition_variable wait_pause_cv;
|
||||
std::condition_variable wait_signal_cv;
|
||||
mutable std::mutex event_mutex;
|
||||
mutable std::mutex sequence_mutex;
|
||||
|
||||
std::atomic<bool> paused_state{};
|
||||
bool is_paused{};
|
||||
bool shutting_down{};
|
||||
bool is_multicore{};
|
||||
size_t pause_count{};
|
||||
|
||||
/// Cycle timing
|
||||
u64 ticks{};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user