mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-23 06:09:59 -06:00
Compare commits
202 Commits
1.2.75
...
Canary-1.2
Author | SHA1 | Date | |
---|---|---|---|
978d2c132b | |||
5d63706cea | |||
732aafd3bb | |||
3fa714bb72 | |||
7c01633f13 | |||
27c5cba10b | |||
3525d5ecd4 | |||
6286501550 | |||
61ae427a4d | |||
19d2883a35 | |||
617c03119f | |||
e43d899e1d | |||
0cd09ea0c5 | |||
4135d74e4d | |||
bd29f658b1 | |||
df150f0788 | |||
e50198b37d | |||
f426945fec | |||
172869bfba | |||
b6f88514f9 | |||
e92f52e56c | |||
318498eab0 | |||
a5cde8e006 | |||
d0a344d632 | |||
ca66298817 | |||
9ae1c4380d | |||
c88518bce2 | |||
a3888ed7cf | |||
7cbbd02973 | |||
b2e1e553e4 | |||
699e1962b1 | |||
e486b902b1 | |||
0ab5b41c4b | |||
d10a478cce | |||
ec1020b165 | |||
4082ebad1a | |||
da8ea06074 | |||
7f9dccb293 | |||
8e4a77aba0 | |||
8fd8a776c9 | |||
eec92c242c | |||
42a739d34c | |||
f362bef43d | |||
f5ce539de9 | |||
4f699afe7a | |||
6caab1aa37 | |||
9baaa2b8f8 | |||
7f376b4f45 | |||
32cdccde12 | |||
cbd851d00e | |||
f463ea1c5d | |||
1dd69912b1 | |||
1fbb0d8e7d | |||
9ee3f1ff36 | |||
d052d74ac4 | |||
df91c4c57a | |||
2aaaa7872f | |||
b5999583d6 | |||
8b3a945b5f | |||
09107b67ff | |||
12b264af44 | |||
0c21b07f19 | |||
18625cf775 | |||
77a9246825 | |||
709eeda94a | |||
7d54424048 | |||
664c63c6a8 | |||
153d1ef06b | |||
e1e4e5d2d5 | |||
44ee4190e6 | |||
9408452f93 | |||
38833ff60a | |||
1faa72f22f | |||
56e45ae648 | |||
07074272ca | |||
9eb273a0f7 | |||
ccddaa77d1 | |||
01c2e67334 | |||
4c646721d6 | |||
d3bc3a1081 | |||
c69881a0a2 | |||
1bc0159139 | |||
0733b7d0a1 | |||
9df1366fa1 | |||
c73b5bdf46 | |||
9754d247b5 | |||
267e9f6350 | |||
d7b3dd12d1 | |||
c9b2a6b1f1 | |||
17233d30da | |||
2bf48f57d2 | |||
412d4065b8 | |||
e6644626fc | |||
0bacdb8765 | |||
0ca4d6e921 | |||
f0aa7eedf6 | |||
41acc4b1f3 | |||
7aede70ba9 | |||
a0a4f78cff | |||
16a60fdf12 | |||
4d7350fc6e | |||
b05eab21a2 | |||
ff667a5c84 | |||
2f540dc88c | |||
3cb996bf5c | |||
852823104f | |||
3094df54dd | |||
278fe9d4f0 | |||
a560d2efdb | |||
a270dc721c | |||
23b0b22400 | |||
3dfbf55611 | |||
cb355f504d | |||
b5483d8fe0 | |||
8259f790d7 | |||
1ea345faa7 | |||
5913ceda40 | |||
decd37ce6d | |||
67ec10feea | |||
4c7cb54ec6 | |||
f898a5ecf4 | |||
2fac0f4db1 | |||
0f18df982f | |||
d9fe0da345 | |||
1f0fa525a3 | |||
e15a207656 | |||
77ef82d92a | |||
ba199f4325 | |||
4171913baf | |||
5b36a9cf9f | |||
a460eda195 | |||
c77c1acd08 | |||
d68295e57d | |||
bb3d95722e | |||
2fa3a7bfa1 | |||
381921390a | |||
221524d879 | |||
9cddf3b66b | |||
0adaa4cb96 | |||
ff6628149d | |||
8db5a7e98b | |||
8e00cb5232 | |||
362f62cd39 | |||
2cd54d0da0 | |||
b97b8ca8f5 | |||
d7d4225e0d | |||
a9e0fac9dc | |||
1b9656e960 | |||
8994e7476c | |||
b3944a18b7 | |||
18c957f90b | |||
1a005f96e7 | |||
072cd2824a | |||
9da97bc911 | |||
39252b7267 | |||
ec11bf2af9 | |||
8ae72c1a00 | |||
06abba25c1 | |||
de00a71690 | |||
315a1819c0 | |||
4ffb8aef12 | |||
290a6ad5de | |||
eda4f4349b | |||
5fbcb1f3a7 | |||
d00754477e | |||
0bc1eddaeb | |||
baad1e313f | |||
a1e6d11dcb | |||
3d168a8bfa | |||
000c1756de | |||
1d0152b961 | |||
07690e4527 | |||
08b7257be5 | |||
17483aad24 | |||
6b5cb151c3 | |||
3680df6092 | |||
facc12a94a | |||
8e55e6d6d7 | |||
346dfe9542 | |||
8a2b56cae6 | |||
baf179efdb | |||
2a72fb2088 | |||
0caeab2270 | |||
f72d2c1b2b | |||
a18cecbc30 | |||
2e6794e69b | |||
7e16fccfc1 | |||
a81212bbf1 | |||
e8d3ad4d8b | |||
3b6731a351 | |||
e653848a2c | |||
5534001152 | |||
e05875a079 | |||
49eeb26b6f | |||
f8d63f9a2f | |||
e2b7738465 | |||
1d42c29335 | |||
c2de5cc700 | |||
aaaf60b7a4 | |||
150e06e0de | |||
c0a4d95c5d | |||
c3831428e0 |
10
.github/labeler.yml
vendored
10
.github/labeler.yml
vendored
@ -32,4 +32,12 @@ kernel:
|
|||||||
|
|
||||||
infra:
|
infra:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props']
|
- any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props', 'src/Ryujinx.BuildValidationTasks/**']
|
||||||
|
|
||||||
|
documentation:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: 'docs/**'
|
||||||
|
|
||||||
|
ldn:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: 'src/Ryujinx.HLE/HOS/Services/Ldn/**'
|
||||||
|
23
.github/workflows/build.yml
vendored
23
.github/workflows/build.yml
vendored
@ -64,14 +64,9 @@ jobs:
|
|||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
|
||||||
|
|
||||||
- name: Set executable bit
|
- name: Set executable bit
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
|
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
@ -119,13 +114,6 @@ jobs:
|
|||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage
|
||||||
path: publish_appimage
|
path: publish_appimage
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
|
||||||
path: publish_sdl2_headless
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
|
||||||
|
|
||||||
build_macos:
|
build_macos:
|
||||||
name: macOS Universal (${{ matrix.configuration }})
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -171,20 +159,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
|
||||||
run: |
|
|
||||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||||
path: "publish/*.tar.gz"
|
path: "publish/*.tar.gz"
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
|
||||||
path: "publish_headless/*.tar.gz"
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
|
66
.github/workflows/canary.yml
vendored
66
.github/workflows/canary.yml
vendored
@ -21,9 +21,9 @@ env:
|
|||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
RYUJINX_BASE_VERSION: "1.2"
|
RYUJINX_BASE_VERSION: "1.2"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
|
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary"
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Canary-Releases"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -43,8 +43,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
owner: context.repo.owner,
|
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}",
|
||||||
repo: context.repo.repo,
|
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}",
|
||||||
ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}',
|
ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}',
|
||||||
sha: context.sha
|
sha: context.sha
|
||||||
})
|
})
|
||||||
@ -54,7 +54,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
|
body: |
|
||||||
|
# Canary builds:
|
||||||
|
|
||||||
|
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ github.repository }}/releases/latest) instead if that sounds like something you don't want to deal with.
|
||||||
|
|
||||||
|
| Platform | Artifact |
|
||||||
|
|--|--|
|
||||||
|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||||
|
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||||
|
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
|
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
@ -103,20 +115,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.platform.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_ava
|
||||||
rm publish/libarmeilleure-jitsupport.dylib
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||||
popd
|
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
|
||||||
rm publish/libarmeilleure-jitsupport.dylib
|
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -126,13 +132,7 @@ jobs:
|
|||||||
pushd publish_ava
|
pushd publish_ava
|
||||||
rm publish/libarmeilleure-jitsupport.dylib
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||||
popd
|
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
|
||||||
rm publish/libarmeilleure-jitsupport.dylib
|
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
|
||||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -179,9 +179,21 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||||
#artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*"
|
#artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
|
body: |
|
||||||
|
# Canary builds:
|
||||||
|
|
||||||
|
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ github.repository }}/releases/latest) instead if that sounds like something you don't want to deal with.
|
||||||
|
|
||||||
|
| Platform | Artifact |
|
||||||
|
|--|--|
|
||||||
|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||||
|
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||||
|
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
|
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
@ -236,19 +248,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
|
||||||
run: |
|
|
||||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
|
||||||
|
|
||||||
- name: Pushing new release
|
- name: Pushing new release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
||||||
artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
|
artifacts: "publish_ava/*.tar.gz"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
|
body: ""
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
|
12
.github/workflows/nightly_pr_comment.yml
vendored
12
.github/workflows/nightly_pr_comment.yml
vendored
@ -37,21 +37,17 @@ jobs:
|
|||||||
if (!artifacts.length) {
|
if (!artifacts.length) {
|
||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
let body = `*You need to be logged into GitHub to download these files.*\n\nDownload the artifacts for this pull request:\n`;
|
||||||
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
|
const url = `https://github.com/Ryubing/Ryujinx/actions/runs/${run_id}/artifacts/${art.id}`;
|
||||||
if(art.name.includes('Debug')) {
|
if(art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
|
||||||
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
|
||||||
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
|
||||||
} else {
|
} else {
|
||||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
body += `\n* [${art.name}](${url})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hidden_headless_artifacts += `\n</details>`;
|
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
hidden_debug_artifacts += `\n</details>`;
|
||||||
body += hidden_headless_artifacts;
|
|
||||||
body += hidden_debug_artifacts;
|
body += hidden_debug_artifacts;
|
||||||
|
|
||||||
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
||||||
|
51
.github/workflows/release.yml
vendored
51
.github/workflows/release.yml
vendored
@ -21,7 +21,7 @@ env:
|
|||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
RYUJINX_BASE_VERSION: "1.2"
|
RYUJINX_BASE_VERSION: "1.2"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
@ -42,8 +42,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
owner: context.repo.owner,
|
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}",
|
||||||
repo: context.repo.repo,
|
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}",
|
||||||
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||||
sha: context.sha
|
sha: context.sha
|
||||||
})
|
})
|
||||||
@ -53,7 +53,16 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
|
body: |
|
||||||
|
# Stable builds:
|
||||||
|
| Platform | Artifact |
|
||||||
|
|--|--|
|
||||||
|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||||
|
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||||
|
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
|
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
@ -102,8 +111,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.platform.os == 'windows-latest'
|
||||||
@ -112,11 +120,6 @@ jobs:
|
|||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
|
||||||
rm libarmeilleure-jitsupport.dylib
|
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
|
||||||
popd
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
@ -163,11 +166,6 @@ jobs:
|
|||||||
chmod +x Ryujinx.sh Ryujinx
|
chmod +x Ryujinx.sh Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
|
||||||
chmod +x Ryujinx.sh Ryujinx.Headless.SDL2
|
|
||||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
|
||||||
popd
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Pushing new release
|
- name: Pushing new release
|
||||||
@ -176,7 +174,16 @@ jobs:
|
|||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
|
body: |
|
||||||
|
# Stable builds:
|
||||||
|
| Platform | Artifact |
|
||||||
|
|--|--|
|
||||||
|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||||
|
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||||
|
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
|
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
@ -231,19 +238,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
|
||||||
run: |
|
|
||||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
|
||||||
|
|
||||||
- name: Pushing new release
|
- name: Pushing new release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz"
|
artifacts: "publish/*.tar.gz"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
|
body: ""
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -175,3 +175,6 @@ PublishProfiles/
|
|||||||
|
|
||||||
# Glade backup files
|
# Glade backup files
|
||||||
*.glade~
|
*.glade~
|
||||||
|
|
||||||
|
# Ignore MacOS Attribute Files
|
||||||
|
._*
|
||||||
|
@ -5,7 +5,7 @@ If you wish to build the emulator yourself, follow these steps:
|
|||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
|
|
||||||
Install the [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
|
Install the [.NET 9.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/9.0).
|
||||||
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
|
6
Directory.Build.props
Normal file
6
Directory.Build.props
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
@ -10,10 +10,14 @@
|
|||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
|
||||||
|
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
||||||
|
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
|
||||||
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.4.0" />
|
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.4.0" />
|
||||||
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/>
|
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/>
|
||||||
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
|
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
|
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
||||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
<PackageVersion Include="Concentus" Version="2.2.0" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
||||||
@ -38,9 +42,10 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||||
<PackageVersion Include="Gommon" Version="2.6.5" />
|
<PackageVersion Include="Gommon" Version="2.7.0.1" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
|
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
||||||
@ -48,8 +53,8 @@
|
|||||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
<PackageVersion Include="System.Management" Version="9.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
49
README.md
49
README.md
@ -1,35 +1,26 @@
|
|||||||
<h1 align="center">
|
<table align="center">
|
||||||
|
<tr>
|
||||||
|
<td align="center" width="25%">
|
||||||
|
<img src="https://raw.githubusercontent.com/GreemDev/ryuassets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
|
||||||
|
</td>
|
||||||
|
<td align="center" width="75%">
|
||||||
|
|
||||||
|
# Ryujinx
|
||||||
|
|
||||||
|
[](https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml)
|
||||||
|
[](https://github.com/GreemDev/Ryujinx/releases/latest)
|
||||||
<br>
|
<br>
|
||||||
<img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/master/distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a>
|
[](https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml)
|
||||||
<br>
|
[](https://github.com/GreemDev/Ryujinx-Canary/releases/latest)
|
||||||
<b>Ryujinx</b>
|
</td>
|
||||||
<br>
|
</tr>
|
||||||
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
</table>
|
||||||
<br>
|
|
||||||
<a href="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml">
|
|
||||||
<img src="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml/badge.svg"
|
|
||||||
alt="">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/GreemDev/Ryujinx/releases/latest">
|
|
||||||
<img src="https://img.shields.io/github/v/release/GreemDev/Ryujinx"
|
|
||||||
alt="Latest Release">
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<a href="https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml">
|
|
||||||
<img src="https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml/badge.svg"
|
|
||||||
alt="">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/GreemDev/Ryujinx-Canary/releases/latest">
|
|
||||||
<img src="https://img.shields.io/github/v/release/GreemDev/Ryujinx-Canary?label=canary"
|
|
||||||
alt="Latest Canary Release">
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
It was written from scratch and development on the project began in September 2017.
|
It was written from scratch and development on the project began in September 2017.
|
||||||
Ryujinx is available on Github under the <a href="https://github.com/GreemDev/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
Ryujinx is available on GitHub under the <a href="https://github.com/GreemDev/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -42,7 +33,7 @@
|
|||||||
Guides and documentation can be found on the <a href="https://github.com/GreemDev/Ryujinx/wiki">Wiki tab</a>.
|
Guides and documentation can be found on the <a href="https://github.com/GreemDev/Ryujinx/wiki">Wiki tab</a>.
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
If you would like a version more preservative fork of Ryujinx, check out <a href="https://github.com/ryujinx-mirror/ryujinx">ryujinx-mirror</a>.
|
If you would like a more preservative fork of Ryujinx, check out <a href="https://github.com/ryujinx-mirror/ryujinx">ryujinx-mirror</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -63,7 +54,7 @@ failing to meet this requirement may result in a poor gameplay experience or une
|
|||||||
|
|
||||||
## Latest build
|
## Latest build
|
||||||
|
|
||||||
Stable builds are made every so often onto a separate "release" branch that then gets put into the releases you know and love.
|
Stable builds are made every so often onto a separate "release" branch that then gets put into the releases you know and love.
|
||||||
These stable builds exist so that the end user can get a more **enjoyable and stable experience**.
|
These stable builds exist so that the end user can get a more **enjoyable and stable experience**.
|
||||||
|
|
||||||
You can find the latest stable release [here](https://github.com/GreemDev/Ryujinx/releases/latest).
|
You can find the latest stable release [here](https://github.com/GreemDev/Ryujinx/releases/latest).
|
||||||
@ -91,7 +82,7 @@ If you are planning to contribute or just want to learn more about this project
|
|||||||
It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
|
It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
|
||||||
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster).
|
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster).
|
||||||
The fastest option (host, unchecked) is set by default.
|
The fastest option (host, unchecked) is set by default.
|
||||||
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads.
|
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads.
|
||||||
The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game.
|
The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game.
|
||||||
NOTE: This feature is enabled by default in the Options menu > System tab.
|
NOTE: This feature is enabled by default in the Options menu > System tab.
|
||||||
You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch!
|
You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch!
|
||||||
|
37
Ryujinx.sln
37
Ryujinx.sln
@ -57,14 +57,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}"
|
||||||
@ -81,13 +77,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}"
|
||||||
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
|
.github\workflows\build.yml = .github\workflows\build.yml
|
||||||
|
.github\workflows\canary.yml = .github\workflows\canary.yml
|
||||||
Directory.Packages.props = Directory.Packages.props
|
Directory.Packages.props = Directory.Packages.props
|
||||||
.github/workflows/release.yml = .github/workflows/release.yml
|
.github\workflows\release.yml = .github\workflows\release.yml
|
||||||
.github/workflows/canary.yml = .github/workflows/canary.yml
|
|
||||||
.github/workflows/build.yml = .github/workflows/build.yml
|
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
@ -204,10 +209,6 @@ Global
|
|||||||
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@ -216,10 +217,6 @@ Global
|
|||||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@ -252,6 +249,16 @@ Global
|
|||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -14,7 +14,7 @@ if [ -z "$RYUJINX_BIN" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
COMMAND="env LANG=C.UTF-8 DOTNET_EnableAlternateStackCheck=1"
|
||||||
|
|
||||||
if command -v gamemoderun > /dev/null 2>&1; then
|
if command -v gamemoderun > /dev/null 2>&1; then
|
||||||
COMMAND="$COMMAND gamemoderun"
|
COMMAND="$COMMAND gamemoderun"
|
||||||
|
@ -13,7 +13,7 @@ mkdir -p AppDir/usr/bin
|
|||||||
|
|
||||||
cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop
|
cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop
|
||||||
cp distribution/linux/appimage/AppRun AppDir/AppRun
|
cp distribution/linux/appimage/AppRun AppDir/AppRun
|
||||||
cp src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png AppDir/Ryujinx.svg
|
cp distribution/misc/Logo.svg AppDir/Ryujinx.svg
|
||||||
|
|
||||||
|
|
||||||
cp -r "$BUILDDIR"/* AppDir/usr/bin/
|
cp -r "$BUILDDIR"/* AppDir/usr/bin/
|
||||||
|
@ -40,11 +40,11 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.1</string>
|
<string>1.2</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.1.0</string>
|
<string>1.2.0</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CSResourcesFileMapped</key>
|
<key>CSResourcesFileMapped</key>
|
||||||
|
Binary file not shown.
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ "$#" -lt 7 ]; then
|
if [ "$#" -lt 8 ]; then
|
||||||
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <EXTRA_ARGS>"
|
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <CANARY>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -18,10 +18,11 @@ ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
|
|||||||
VERSION=$5
|
VERSION=$5
|
||||||
SOURCE_REVISION_ID=$6
|
SOURCE_REVISION_ID=$6
|
||||||
CONFIGURATION=$7
|
CONFIGURATION=$7
|
||||||
EXTRA_ARGS=$8
|
CANARY=$8
|
||||||
|
|
||||||
if [ "$VERSION" == "1.1.0" ];
|
if [ "$CANARY" == "1" ]; then
|
||||||
then
|
RELEASE_TAR_FILE_NAME=ryujinx-canary-$VERSION-macos_universal.app.tar
|
||||||
|
elif [ "$VERSION" == "1.1.0" ]; then
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||||
else
|
else
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar
|
||||||
@ -61,7 +62,7 @@ mkdir -p "$OUTPUT_DIRECTORY"
|
|||||||
cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE"
|
cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE"
|
||||||
rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH"
|
rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH"
|
||||||
|
|
||||||
# Make it libraries universal
|
# Make its libraries universal
|
||||||
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib"
|
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib"
|
||||||
|
|
||||||
if ! [ -x "$(command -v lipo)" ];
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ "$#" -lt 7 ]; then
|
if [ "$#" -lt 8 ]; then
|
||||||
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <EXTRA_ARGS>"
|
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <CANARY>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -18,13 +18,14 @@ ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
|
|||||||
VERSION=$5
|
VERSION=$5
|
||||||
SOURCE_REVISION_ID=$6
|
SOURCE_REVISION_ID=$6
|
||||||
CONFIGURATION=$7
|
CONFIGURATION=$7
|
||||||
EXTRA_ARGS=$8
|
CANARY=$8
|
||||||
|
|
||||||
if [ "$VERSION" == "1.1.0" ];
|
if [ "$CANARY" == "1" ]; then
|
||||||
then
|
RELEASE_TAR_FILE_NAME=nogui-ryujinx-canary-$VERSION-macos_universal.tar
|
||||||
RELEASE_TAR_FILE_NAME=sdl2-ryujinx-headless-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar
|
elif [ "$VERSION" == "1.1.0" ]; then
|
||||||
|
RELEASE_TAR_FILE_NAME=nogui-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar
|
||||||
else
|
else
|
||||||
RELEASE_TAR_FILE_NAME=sdl2-ryujinx-headless-$VERSION-macos_universal.tar
|
RELEASE_TAR_FILE_NAME=nogui-ryujinx-$VERSION-macos_universal.tar
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64"
|
ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64"
|
||||||
@ -56,7 +57,7 @@ mkdir -p "$OUTPUT_DIRECTORY"
|
|||||||
cp -R "$ARM64_OUTPUT/" "$UNIVERSAL_OUTPUT"
|
cp -R "$ARM64_OUTPUT/" "$UNIVERSAL_OUTPUT"
|
||||||
rm "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH"
|
rm "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH"
|
||||||
|
|
||||||
# Make it libraries universal
|
# Make its libraries universal
|
||||||
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTPUT" "$X64_OUTPUT" "$UNIVERSAL_OUTPUT" "**/*.dylib"
|
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTPUT" "$X64_OUTPUT" "$UNIVERSAL_OUTPUT" "**/*.dylib"
|
||||||
|
|
||||||
if ! [ -x "$(command -v lipo)" ];
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
|
@ -17,7 +17,7 @@ error_handler() {
|
|||||||
set the button_pressed to the button returned of the result
|
set the button_pressed to the button returned of the result
|
||||||
|
|
||||||
if the button_pressed is \"Open Download Page\" then
|
if the button_pressed is \"Open Download Page\" then
|
||||||
open location \"https://ryujinx.org/download\"
|
open location \"https://ryujinx.app/download\"
|
||||||
end if
|
end if
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -54,4 +54,4 @@ if [ "$#" -le 3 ]; then
|
|||||||
open -a "$INSTALL_DIRECTORY"
|
open -a "$INSTALL_DIRECTORY"
|
||||||
else
|
else
|
||||||
open -a "$INSTALL_DIRECTORY" --args "${APP_ARGUMENTS[@]}"
|
open -a "$INSTALL_DIRECTORY" --args "${APP_ARGUMENTS[@]}"
|
||||||
fi
|
fi
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 MiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "8.0.100",
|
"version": "9.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,252 +0,0 @@
|
|||||||
using ARMeilleure.Diagnostics;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a table of guest address to a value.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
|
||||||
public unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a level in an <see cref="AddressTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly struct Level
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the index of the <see cref="Level"/> in the guest address.
|
|
||||||
/// </summary>
|
|
||||||
public int Index { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the length of the <see cref="Level"/> in the guest address.
|
|
||||||
/// </summary>
|
|
||||||
public int Length { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the mask which masks the bits used by the <see cref="Level"/>.
|
|
||||||
/// </summary>
|
|
||||||
public ulong Mask => ((1ul << Length) - 1) << Index;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Level"/> structure with the specified
|
|
||||||
/// <paramref name="index"/> and <paramref name="length"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">Index of the <see cref="Level"/></param>
|
|
||||||
/// <param name="length">Length of the <see cref="Level"/></param>
|
|
||||||
public Level(int index, int length)
|
|
||||||
{
|
|
||||||
(Index, Length) = (index, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the <see cref="Level"/> from the specified guest <paramref name="address"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns>Value of the <see cref="Level"/> from the specified guest <paramref name="address"/></returns>
|
|
||||||
public int GetValue(ulong address)
|
|
||||||
{
|
|
||||||
return (int)((address & Mask) >> Index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
private TEntry** _table;
|
|
||||||
private readonly List<nint> _pages;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public ulong Mask { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public Level[] Levels { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the default fill value of newly created leaf pages.
|
|
||||||
/// </summary>
|
|
||||||
public TEntry Fill { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
|
||||||
public nint Base
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
||||||
|
|
||||||
lock (_pages)
|
|
||||||
{
|
|
||||||
return (nint)GetRootPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
|
||||||
/// <see cref="Level"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
|
||||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
|
||||||
public AddressTable(Level[] levels)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(levels);
|
|
||||||
|
|
||||||
if (levels.Length < 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Table must be at least 2 levels deep.", nameof(levels));
|
|
||||||
}
|
|
||||||
|
|
||||||
_pages = new List<nint>(capacity: 16);
|
|
||||||
|
|
||||||
Levels = levels;
|
|
||||||
Mask = 0;
|
|
||||||
|
|
||||||
foreach (var level in Levels)
|
|
||||||
{
|
|
||||||
Mask |= level.Mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
|
||||||
/// <see cref="AddressTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
|
||||||
public bool IsValid(ulong address)
|
|
||||||
{
|
|
||||||
return (address & ~Mask) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
|
||||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
|
||||||
public ref TEntry GetValue(ulong address)
|
|
||||||
{
|
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
||||||
|
|
||||||
if (!IsValid(address))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_pages)
|
|
||||||
{
|
|
||||||
return ref GetPage(address)[Levels[^1].GetValue(address)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the leaf page for the specified guest <paramref name="address"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
|
|
||||||
private TEntry* GetPage(ulong address)
|
|
||||||
{
|
|
||||||
TEntry** page = GetRootPage();
|
|
||||||
|
|
||||||
for (int i = 0; i < Levels.Length - 1; i++)
|
|
||||||
{
|
|
||||||
ref Level level = ref Levels[i];
|
|
||||||
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
|
||||||
|
|
||||||
if (nextPage == null)
|
|
||||||
{
|
|
||||||
ref Level nextLevel = ref Levels[i + 1];
|
|
||||||
|
|
||||||
nextPage = i == Levels.Length - 2 ?
|
|
||||||
(TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true) :
|
|
||||||
(TEntry*)Allocate(1 << nextLevel.Length, nint.Zero, leaf: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
page = (TEntry**)nextPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (TEntry*)page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
|
|
||||||
private TEntry** GetRootPage()
|
|
||||||
{
|
|
||||||
if (_table == null)
|
|
||||||
{
|
|
||||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, fill: nint.Zero, leaf: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allocates a block of memory of the specified type and length.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of elements</typeparam>
|
|
||||||
/// <param name="length">Number of elements</param>
|
|
||||||
/// <param name="fill">Fill value</param>
|
|
||||||
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
|
||||||
/// <returns>Allocated block</returns>
|
|
||||||
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
|
||||||
{
|
|
||||||
var size = sizeof(T) * length;
|
|
||||||
var page = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
|
||||||
var span = new Span<T>((void*)page, length);
|
|
||||||
|
|
||||||
span.Fill(fill);
|
|
||||||
|
|
||||||
_pages.Add(page);
|
|
||||||
|
|
||||||
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
|
||||||
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
|
|
||||||
/// instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_disposed)
|
|
||||||
{
|
|
||||||
foreach (var page in _pages)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
~AddressTable()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a level in an <see cref="IAddressTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct AddressTableLevel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index of the <see cref="Level"/> in the guest address.
|
||||||
|
/// </summary>
|
||||||
|
public int Index { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the length of the <see cref="AddressTableLevel"/> in the guest address.
|
||||||
|
/// </summary>
|
||||||
|
public int Length { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the mask which masks the bits used by the <see cref="AddressTableLevel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ulong Mask => ((1ul << Length) - 1) << Index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddressTableLevel"/> structure with the specified
|
||||||
|
/// <paramref name="index"/> and <paramref name="length"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the <see cref="AddressTableLevel"/></param>
|
||||||
|
/// <param name="length">Length of the <see cref="AddressTableLevel"/></param>
|
||||||
|
public AddressTableLevel(int index, int length)
|
||||||
|
{
|
||||||
|
(Index, Length) = (index, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns>Value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/></returns>
|
||||||
|
public long GetValue(ulong address)
|
||||||
|
{
|
||||||
|
return (long)((address & Mask) >> Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
75
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
public static class AddressTablePresets
|
||||||
|
{
|
||||||
|
private static readonly AddressTableLevel[] _levels64Bit =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new(31, 17),
|
||||||
|
new(23, 8),
|
||||||
|
new(15, 8),
|
||||||
|
new( 7, 8),
|
||||||
|
new( 2, 5),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32Bit =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new(31, 17),
|
||||||
|
new(23, 8),
|
||||||
|
new(15, 8),
|
||||||
|
new( 7, 8),
|
||||||
|
new( 1, 6),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 11, 28),
|
||||||
|
new( 2, 9),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 10, 22),
|
||||||
|
new( 1, 9),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 38, 1),
|
||||||
|
new( 2, 36),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 31, 1),
|
||||||
|
new( 1, 30),
|
||||||
|
};
|
||||||
|
|
||||||
|
//high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization
|
||||||
|
//low power will never run worse than non-sparse, but for most systems it won't be necessary
|
||||||
|
//high power is always used, but I've left low power in here for future reference
|
||||||
|
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
|
||||||
|
{
|
||||||
|
if (sparse)
|
||||||
|
{
|
||||||
|
if (lowPower)
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64Bit : _levels32Bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace ARMeilleure.Common
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
unsafe abstract class Allocator : IDisposable
|
public unsafe abstract class Allocator : IDisposable
|
||||||
{
|
{
|
||||||
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
||||||
{
|
{
|
||||||
|
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// True if the address table's bottom level is sparsely mapped.
|
||||||
|
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
|
||||||
|
/// </summary>
|
||||||
|
bool Sparse { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
ulong Mask { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AddressTableLevel"/>s used by the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
AddressTableLevel[] Levels { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default fill value of newly created leaf pages.
|
||||||
|
/// </summary>
|
||||||
|
TEntry Fill { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
|
nint Base { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||||
|
/// <see cref="IAddressTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
||||||
|
bool IsValid(ulong address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
||||||
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||||
|
ref TEntry GetValue(ulong address);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ARMeilleure.Common
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
unsafe sealed class NativeAllocator : Allocator
|
public unsafe sealed class NativeAllocator : Allocator
|
||||||
{
|
{
|
||||||
public static NativeAllocator Instance { get; } = new();
|
public static NativeAllocator Instance { get; } = new();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.Decoders
|
namespace ARMeilleure.Decoders
|
||||||
{
|
{
|
||||||
@ -149,7 +150,7 @@ namespace ARMeilleure.Decoders
|
|||||||
return (((long)opCode << 45) >> 48) & ~3;
|
return (((long)opCode << 45) >> 48) & ~3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool VectorArgumentsInvalid(bool q, params int[] args)
|
public static bool VectorArgumentsInvalid(bool q, params ReadOnlySpan<int> args)
|
||||||
{
|
{
|
||||||
if (q)
|
if (q)
|
||||||
{
|
{
|
||||||
|
@ -193,6 +193,8 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand hostAddress;
|
Operand hostAddress;
|
||||||
|
|
||||||
|
var table = context.FunctionTable;
|
||||||
|
|
||||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||||
// onto the dispatch stub.
|
// onto the dispatch stub.
|
||||||
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
||||||
@ -203,6 +205,30 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||||
}
|
}
|
||||||
|
else if (table.Sparse)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
// Deliberately attempts to avoid branches.
|
||||||
|
|
||||||
|
Operand tableBase = !context.HasPtc ?
|
||||||
|
Const(table.Base) :
|
||||||
|
Const(table.Base, Ptc.FunctionTableSymbol);
|
||||||
|
|
||||||
|
hostAddress = tableBase;
|
||||||
|
|
||||||
|
for (int i = 0; i < table.Levels.Length; i++)
|
||||||
|
{
|
||||||
|
var level = table.Levels[i];
|
||||||
|
int clearBits = 64 - (level.Index + level.Length);
|
||||||
|
|
||||||
|
Operand index = context.ShiftLeft(
|
||||||
|
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||||
|
Const(3)
|
||||||
|
);
|
||||||
|
|
||||||
|
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hostAddress = !context.HasPtc ?
|
hostAddress = !context.HasPtc ?
|
||||||
|
@ -49,6 +49,9 @@ namespace ARMeilleure.Instructions
|
|||||||
case 0b11_011_1101_0000_011:
|
case 0b11_011_1101_0000_011:
|
||||||
EmitGetTpidrroEl0(context);
|
EmitGetTpidrroEl0(context);
|
||||||
return;
|
return;
|
||||||
|
case 0b11_011_1101_0000_101:
|
||||||
|
EmitGetTpidr2El0(context);
|
||||||
|
return;
|
||||||
case 0b11_011_1110_0000_000:
|
case 0b11_011_1110_0000_000:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0));
|
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0));
|
||||||
break;
|
break;
|
||||||
@ -84,6 +87,9 @@ namespace ARMeilleure.Instructions
|
|||||||
case 0b11_011_1101_0000_010:
|
case 0b11_011_1101_0000_010:
|
||||||
EmitSetTpidrEl0(context);
|
EmitSetTpidrEl0(context);
|
||||||
return;
|
return;
|
||||||
|
case 0b11_011_1101_0000_101:
|
||||||
|
EmitSetTpidr2El0(context);
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||||
@ -213,6 +219,17 @@ namespace ARMeilleure.Instructions
|
|||||||
SetIntOrZR(context, op.Rt, result);
|
SetIntOrZR(context, op.Rt, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitGetTpidr2El0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidr2El0Offset())));
|
||||||
|
|
||||||
|
SetIntOrZR(context, op.Rt, result);
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitSetNzcv(ArmEmitterContext context)
|
private static void EmitSetNzcv(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
@ -274,5 +291,16 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
|
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitSetTpidr2El0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand value = GetIntOrZR(context, op.Rt);
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidr2El0Offset())), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ namespace ARMeilleure.Instructions
|
|||||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
|
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params V128[] tb)
|
private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params ReadOnlySpan<V128> tb)
|
||||||
{
|
{
|
||||||
byte[] res = new byte[16];
|
byte[] res = new byte[16];
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operation Operation(Intrinsic intrin, Operand dest, params Operand[] srcs)
|
public static Operation Operation(Intrinsic intrin, Operand dest, params ReadOnlySpan<Operand> srcs)
|
||||||
{
|
{
|
||||||
Operation result = Make(Instruction.Extended, 0, srcs.Length);
|
Operation result = Make(Instruction.Extended, 0, srcs.Length);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
|||||||
{
|
{
|
||||||
public static class NativeSignalHandlerGenerator
|
public static class NativeSignalHandlerGenerator
|
||||||
{
|
{
|
||||||
public const int MaxTrackedRanges = 8;
|
public const int MaxTrackedRanges = 16;
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
private const int StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
|
@ -21,6 +21,7 @@ namespace ARMeilleure.State
|
|||||||
public ulong ExclusiveValueLow;
|
public ulong ExclusiveValueLow;
|
||||||
public ulong ExclusiveValueHigh;
|
public ulong ExclusiveValueHigh;
|
||||||
public int Running;
|
public int Running;
|
||||||
|
public long Tpidr2El0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NativeCtxStorage _dummyStorage = new();
|
private static NativeCtxStorage _dummyStorage = new();
|
||||||
@ -176,6 +177,9 @@ namespace ARMeilleure.State
|
|||||||
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
|
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
|
||||||
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
|
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
|
||||||
|
|
||||||
|
public long GetTpidr2El0() => GetStorage().Tpidr2El0;
|
||||||
|
public void SetTpidr2El0(long value) => GetStorage().Tpidr2El0 = value;
|
||||||
|
|
||||||
public int GetCounter() => GetStorage().Counter;
|
public int GetCounter() => GetStorage().Counter;
|
||||||
public void SetCounter(int value) => GetStorage().Counter = value;
|
public void SetCounter(int value) => GetStorage().Counter = value;
|
||||||
|
|
||||||
@ -232,6 +236,11 @@ namespace ARMeilleure.State
|
|||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetTpidr2El0Offset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Tpidr2El0);
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetCounterOffset()
|
public static int GetCounterOffset()
|
||||||
{
|
{
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
||||||
|
@ -46,7 +46,7 @@ namespace ARMeilleure.Translation
|
|||||||
public IMemoryManager Memory { get; }
|
public IMemoryManager Memory { get; }
|
||||||
|
|
||||||
public EntryTable<uint> CountTable { get; }
|
public EntryTable<uint> CountTable { get; }
|
||||||
public AddressTable<ulong> FunctionTable { get; }
|
public IAddressTable<ulong> FunctionTable { get; }
|
||||||
public TranslatorStubs Stubs { get; }
|
public TranslatorStubs Stubs { get; }
|
||||||
|
|
||||||
public ulong EntryAddress { get; }
|
public ulong EntryAddress { get; }
|
||||||
@ -62,7 +62,7 @@ namespace ARMeilleure.Translation
|
|||||||
public ArmEmitterContext(
|
public ArmEmitterContext(
|
||||||
IMemoryManager memory,
|
IMemoryManager memory,
|
||||||
EntryTable<uint> countTable,
|
EntryTable<uint> countTable,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<ulong> funcTable,
|
||||||
TranslatorStubs stubs,
|
TranslatorStubs stubs,
|
||||||
ulong entryAddress,
|
ulong entryAddress,
|
||||||
bool highCq,
|
bool highCq,
|
||||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
@ -26,7 +27,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = new();
|
private static readonly List<CacheEntry> _cacheEntries = new();
|
||||||
|
|
||||||
private static readonly object _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
|
@ -559,27 +559,27 @@ namespace ARMeilleure.Translation
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand AddIntrinsic(Intrinsic intrin, params Operand[] args)
|
public Operand AddIntrinsic(Intrinsic intrin, params ReadOnlySpan<Operand> args)
|
||||||
{
|
{
|
||||||
return Add(intrin, Local(OperandType.V128), args);
|
return Add(intrin, Local(OperandType.V128), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand AddIntrinsicInt(Intrinsic intrin, params Operand[] args)
|
public Operand AddIntrinsicInt(Intrinsic intrin, params ReadOnlySpan<Operand> args)
|
||||||
{
|
{
|
||||||
return Add(intrin, Local(OperandType.I32), args);
|
return Add(intrin, Local(OperandType.I32), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand AddIntrinsicLong(Intrinsic intrin, params Operand[] args)
|
public Operand AddIntrinsicLong(Intrinsic intrin, params ReadOnlySpan<Operand> args)
|
||||||
{
|
{
|
||||||
return Add(intrin, Local(OperandType.I64), args);
|
return Add(intrin, Local(OperandType.I64), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddIntrinsicNoRet(Intrinsic intrin, params Operand[] args)
|
public void AddIntrinsicNoRet(Intrinsic intrin, params ReadOnlySpan<Operand> args)
|
||||||
{
|
{
|
||||||
Add(intrin, default, args);
|
Add(intrin, default, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Operand Add(Intrinsic intrin, Operand dest, params Operand[] sources)
|
private Operand Add(Intrinsic intrin, Operand dest, params ReadOnlySpan<Operand> sources)
|
||||||
{
|
{
|
||||||
NewNextBlockIfNeeded();
|
NewNextBlockIfNeeded();
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 6997; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
@ -41,6 +41,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||||
|
public static readonly Symbol FunctionTableSymbol = new(SymbolType.Special, 4);
|
||||||
|
|
||||||
private const byte FillingByte = 0x00;
|
private const byte FillingByte = 0x00;
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
@ -58,7 +59,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private readonly ManualResetEvent _waitEvent;
|
private readonly ManualResetEvent _waitEvent;
|
||||||
|
|
||||||
private readonly object _lock;
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@ -88,8 +89,6 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
_waitEvent = new ManualResetEvent(true);
|
_waitEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
_lock = new object();
|
|
||||||
|
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
|
|
||||||
TitleIdText = TitleIdTextDefault;
|
TitleIdText = TitleIdTextDefault;
|
||||||
@ -101,7 +100,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Disable();
|
Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
|
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
||||||
{
|
{
|
||||||
Wait();
|
Wait();
|
||||||
|
|
||||||
@ -127,6 +126,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||||
_memoryMode = memoryMode;
|
_memoryMode = memoryMode;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}");
|
||||||
|
|
||||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
||||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
||||||
|
|
||||||
@ -140,8 +141,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Directory.CreateDirectory(workPathBackup);
|
Directory.CreateDirectory(workPathBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
|
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
||||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
||||||
|
|
||||||
PreLoad();
|
PreLoad();
|
||||||
Profiler.PreLoad();
|
Profiler.PreLoad();
|
||||||
@ -706,6 +707,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
imm = translator.Stubs.DispatchStub;
|
imm = translator.Stubs.DispatchStub;
|
||||||
}
|
}
|
||||||
|
else if (symbol == FunctionTableSymbol)
|
||||||
|
{
|
||||||
|
imm = translator.FunctionTable.Base;
|
||||||
|
}
|
||||||
|
|
||||||
if (imm == null)
|
if (imm == null)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
|
using Humanizer;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
@ -41,7 +42,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private readonly ManualResetEvent _waitEvent;
|
private readonly ManualResetEvent _waitEvent;
|
||||||
|
|
||||||
private readonly object _lock;
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@ -58,15 +59,13 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
_ptc = ptc;
|
_ptc = ptc;
|
||||||
|
|
||||||
_timer = new Timer(SaveInterval * 1000d);
|
_timer = new Timer(SaveInterval.Seconds());
|
||||||
_timer.Elapsed += PreSave;
|
_timer.Elapsed += TimerElapsed;
|
||||||
|
|
||||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||||
|
|
||||||
_waitEvent = new ManualResetEvent(true);
|
_waitEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
_lock = new object();
|
|
||||||
|
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
|
|
||||||
ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
|
ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
|
||||||
@ -74,6 +73,9 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Enabled = false;
|
Enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TimerElapsed(object _, ElapsedEventArgs __)
|
||||||
|
=> new Thread(PreSave) { Name = "Ptc.DiskWriter" }.Start();
|
||||||
|
|
||||||
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
@ -264,7 +266,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
compressedStream.SetLength(0L);
|
compressedStream.SetLength(0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PreSave(object source, ElapsedEventArgs e)
|
private void PreSave()
|
||||||
{
|
{
|
||||||
_waitEvent.Reset();
|
_waitEvent.Reset();
|
||||||
|
|
||||||
@ -430,7 +432,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
_timer.Elapsed -= PreSave;
|
_timer.Elapsed -= TimerElapsed;
|
||||||
_timer.Dispose();
|
_timer.Dispose();
|
||||||
|
|
||||||
Wait();
|
Wait();
|
||||||
|
@ -22,33 +22,13 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
public class Translator
|
public class Translator
|
||||||
{
|
{
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 2, 5),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 1, 6),
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly IJitMemoryAllocator _allocator;
|
private readonly IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
private readonly Ptc _ptc;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
internal AddressTable<ulong> FunctionTable { get; }
|
internal IAddressTable<ulong> FunctionTable { get; }
|
||||||
internal EntryTable<uint> CountTable { get; }
|
internal EntryTable<uint> CountTable { get; }
|
||||||
internal TranslatorStubs Stubs { get; }
|
internal TranslatorStubs Stubs { get; }
|
||||||
internal TranslatorQueue Queue { get; }
|
internal TranslatorQueue Queue { get; }
|
||||||
@ -57,7 +37,7 @@ namespace ARMeilleure.Translation
|
|||||||
private Thread[] _backgroundTranslationThreads;
|
private Thread[] _backgroundTranslationThreads;
|
||||||
private volatile int _threadCount;
|
private volatile int _threadCount;
|
||||||
|
|
||||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
_allocator = allocator;
|
_allocator = allocator;
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
@ -72,15 +52,15 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
CountTable = new EntryTable<uint>();
|
CountTable = new EntryTable<uint>();
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
FunctionTable = functionTable;
|
||||||
Stubs = new TranslatorStubs(FunctionTable);
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
||||||
return _ptc;
|
return _ptc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly AddressTable<ulong> _functionTable;
|
private readonly IAddressTable<ulong> _functionTable;
|
||||||
private readonly Lazy<nint> _dispatchStub;
|
private readonly Lazy<nint> _dispatchStub;
|
||||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||||
private readonly Lazy<WrapperFunction> _contextWrapper;
|
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||||
@ -86,7 +86,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(AddressTable<ulong> functionTable)
|
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using Ryujinx.Memory;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Backends.OpenAL
|
namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
{
|
{
|
||||||
@ -18,7 +19,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
private float _volume;
|
private float _volume;
|
||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -11,7 +11,7 @@ namespace Ryujinx.Audio
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock used to control the waiters registration.
|
/// Lock used to control the waiters registration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Events signaled when the driver played audio buffers.
|
/// Events signaled when the driver played audio buffers.
|
||||||
|
@ -2,6 +2,7 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Backends.Common
|
namespace Ryujinx.Audio.Backends.Common
|
||||||
{
|
{
|
||||||
@ -12,7 +13,7 @@ namespace Ryujinx.Audio.Backends.Common
|
|||||||
{
|
{
|
||||||
private const int RingBufferAlignment = 2048;
|
private const int RingBufferAlignment = 2048;
|
||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
private MemoryOwner<byte> _bufferOwner;
|
private MemoryOwner<byte> _bufferOwner;
|
||||||
private Memory<byte> _buffer;
|
private Memory<byte> _buffer;
|
||||||
|
@ -9,20 +9,12 @@ namespace Ryujinx.Audio.Backends.Dummy
|
|||||||
{
|
{
|
||||||
public class DummyHardwareDeviceDriver : IHardwareDeviceDriver
|
public class DummyHardwareDeviceDriver : IHardwareDeviceDriver
|
||||||
{
|
{
|
||||||
private readonly ManualResetEvent _updateRequiredEvent;
|
private readonly ManualResetEvent _updateRequiredEvent = new(false);
|
||||||
private readonly ManualResetEvent _pauseEvent;
|
private readonly ManualResetEvent _pauseEvent = new(true);
|
||||||
|
|
||||||
public static bool IsSupported => true;
|
public static bool IsSupported => true;
|
||||||
|
|
||||||
public float Volume { get; set; }
|
public float Volume { get; set; } = 1f;
|
||||||
|
|
||||||
public DummyHardwareDeviceDriver()
|
|
||||||
{
|
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
|
||||||
_pauseEvent = new ManualResetEvent(true);
|
|
||||||
|
|
||||||
Volume = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||||
{
|
{
|
||||||
@ -60,7 +52,7 @@ namespace Ryujinx.Audio.Backends.Dummy
|
|||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
private void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Input
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AudioInputManager : IDisposable
|
public class AudioInputManager : IDisposable
|
||||||
{
|
{
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock used for session allocation.
|
/// Lock used for session allocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _sessionLock = new();
|
private readonly Lock _sessionLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session ids allocation table.
|
/// The session ids allocation table.
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Input
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The lock of the parent.
|
/// The lock of the parent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _parentLock;
|
private readonly Lock _parentLock;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The dispose state.
|
/// The dispose state.
|
||||||
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Input
|
|||||||
/// <param name="parentLock">The lock of the manager</param>
|
/// <param name="parentLock">The lock of the manager</param>
|
||||||
/// <param name="deviceSession">The hardware device session</param>
|
/// <param name="deviceSession">The hardware device session</param>
|
||||||
/// <param name="bufferEvent">The buffer release event of the audio input</param>
|
/// <param name="bufferEvent">The buffer release event of the audio input</param>
|
||||||
public AudioInputSystem(AudioInputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
|
public AudioInputSystem(AudioInputManager manager, Lock parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_parentLock = parentLock;
|
_parentLock = parentLock;
|
||||||
|
@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Output
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AudioOutputManager : IDisposable
|
public class AudioOutputManager : IDisposable
|
||||||
{
|
{
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock used for session allocation.
|
/// Lock used for session allocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _sessionLock = new();
|
private readonly Lock _sessionLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session ids allocation table.
|
/// The session ids allocation table.
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Output
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// THe lock of the parent.
|
/// THe lock of the parent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _parentLock;
|
private readonly Lock _parentLock;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The dispose state.
|
/// The dispose state.
|
||||||
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Output
|
|||||||
/// <param name="parentLock">The lock of the manager</param>
|
/// <param name="parentLock">The lock of the manager</param>
|
||||||
/// <param name="deviceSession">The hardware device session</param>
|
/// <param name="deviceSession">The hardware device session</param>
|
||||||
/// <param name="bufferEvent">The buffer release event of the audio output</param>
|
/// <param name="bufferEvent">The buffer release event of the audio output</param>
|
||||||
public AudioOutputSystem(AudioOutputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
|
public AudioOutputSystem(AudioOutputManager manager, Lock parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_parentLock = parentLock;
|
_parentLock = parentLock;
|
||||||
|
@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
public class AudioRenderSystem : IDisposable
|
public class AudioRenderSystem : IDisposable
|
||||||
{
|
{
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
private AudioRendererRenderingDevice _renderingDevice;
|
private AudioRendererRenderingDevice _renderingDevice;
|
||||||
private AudioRendererExecutionMode _executionMode;
|
private AudioRendererExecutionMode _executionMode;
|
||||||
|
@ -19,12 +19,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock used for session allocation.
|
/// Lock used for session allocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _sessionLock = new();
|
private readonly Lock _sessionLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock used to control the <see cref="AudioProcessor"/> running state.
|
/// Lock used to control the <see cref="AudioProcessor"/> running state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _audioProcessorLock = new();
|
private readonly Lock _audioProcessorLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session ids allocation table.
|
/// The session ids allocation table.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Server.Upsampler
|
namespace Ryujinx.Audio.Renderer.Server.Upsampler
|
||||||
{
|
{
|
||||||
@ -16,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Global lock of the object.
|
/// Global lock of the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The upsamplers instances.
|
/// The upsamplers instances.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
7
src/Ryujinx.BuildValidationTasks/IValidationTask.cs
Normal file
7
src/Ryujinx.BuildValidationTasks/IValidationTask.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ryujinx.BuildValidationTasks
|
||||||
|
{
|
||||||
|
public interface IValidationTask
|
||||||
|
{
|
||||||
|
public bool Execute(string projectPath, bool isGitRunner);
|
||||||
|
}
|
||||||
|
}
|
117
src/Ryujinx.BuildValidationTasks/LocalesValidationTask.cs
Normal file
117
src/Ryujinx.BuildValidationTasks/LocalesValidationTask.cs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
|
namespace Ryujinx.BuildValidationTasks
|
||||||
|
{
|
||||||
|
public class LocalesValidationTask : IValidationTask
|
||||||
|
{
|
||||||
|
public LocalesValidationTask() { }
|
||||||
|
|
||||||
|
public bool Execute(string projectPath, bool isGitRunner)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Running Locale Validation Task...");
|
||||||
|
|
||||||
|
string path = projectPath + "src/Ryujinx/Assets/locales.json";
|
||||||
|
string data;
|
||||||
|
|
||||||
|
using (StreamReader sr = new(path))
|
||||||
|
{
|
||||||
|
data = sr.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalesJson json;
|
||||||
|
|
||||||
|
if (isGitRunner && data.Contains("\r\n"))
|
||||||
|
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, build locally to fix...");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
json = JsonSerializer.Deserialize<LocalesJson>(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
throw new JsonException(e.Message); //shorter and easier stacktrace
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool encounteredIssue = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < json.Locales.Count; i++)
|
||||||
|
{
|
||||||
|
LocalesEntry locale = json.Locales[i];
|
||||||
|
|
||||||
|
foreach (string langCode in json.Languages.Where(lang => !locale.Translations.ContainsKey(lang)))
|
||||||
|
{
|
||||||
|
encounteredIssue = true;
|
||||||
|
|
||||||
|
if (!isGitRunner)
|
||||||
|
{
|
||||||
|
locale.Translations.Add(langCode, string.Empty);
|
||||||
|
Console.WriteLine($"Added '{langCode}' to Locale '{locale.ID}'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Missing '{langCode}' in Locale '{locale.ID}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string langCode in json.Languages.Where(lang => locale.Translations.ContainsKey(lang) && lang != "en_US" && locale.Translations[lang] == locale.Translations["en_US"]))
|
||||||
|
{
|
||||||
|
encounteredIssue = true;
|
||||||
|
|
||||||
|
if (!isGitRunner)
|
||||||
|
{
|
||||||
|
locale.Translations[langCode] = string.Empty;
|
||||||
|
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
json.Locales[i] = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGitRunner && encounteredIssue)
|
||||||
|
throw new JsonException("1 or more locales are invalid!");
|
||||||
|
|
||||||
|
JsonSerializerOptions jsonOptions = new JsonSerializerOptions()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
NewLine = "\n",
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
string jsonString = JsonSerializer.Serialize(json, jsonOptions);
|
||||||
|
|
||||||
|
using (StreamWriter sw = new(path))
|
||||||
|
{
|
||||||
|
sw.Write(jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Finished Locale Validation Task!");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LocalesJson
|
||||||
|
{
|
||||||
|
public List<string> Languages { get; set; }
|
||||||
|
public List<LocalesEntry> Locales { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LocalesEntry
|
||||||
|
{
|
||||||
|
public string ID { get; set; }
|
||||||
|
public Dictionary<string, string> Translations { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/Ryujinx.BuildValidationTasks/Program.cs
Normal file
37
src/Ryujinx.BuildValidationTasks/Program.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.BuildValidationTasks
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
// Display the number of command line arguments.
|
||||||
|
if (args.Length == 0)
|
||||||
|
throw new ArgumentException("Error: too few arguments!");
|
||||||
|
|
||||||
|
string path = args[0];
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
throw new ArgumentException("Error: path is null or empty!");
|
||||||
|
|
||||||
|
if (!Path.Exists(path))
|
||||||
|
throw new FileLoadException($"path {{{path}}} does not exist!");
|
||||||
|
|
||||||
|
path = Path.GetFullPath(path);
|
||||||
|
|
||||||
|
if (!Directory.GetDirectories(path).Contains($"{path}src"))
|
||||||
|
throw new FileLoadException($"path {{{path}}} is not a valid ryujinx project!");
|
||||||
|
|
||||||
|
bool isGitRunner = path.Contains("runner") || path.Contains("D:\\a\\Ryujinx\\Ryujinx");
|
||||||
|
if (isGitRunner)
|
||||||
|
Console.WriteLine("Is Git Runner!");
|
||||||
|
|
||||||
|
// Run tasks
|
||||||
|
// Pass extra info needed in the task constructors
|
||||||
|
new LocalesValidationTask().Execute(path, isGitRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuildTarget" AfterTargets="AfterBuild">
|
||||||
|
<Message Text="Running Validation Project" Importance="high" />
|
||||||
|
|
||||||
|
<Exec WorkingDirectory="$(ProjectDir)bin\Debug\$(TargetFramework)\"
|
||||||
|
Command="dotnet Ryujinx.BuildValidationTasks.dll "$(ProjectDir)..\..\\""
|
||||||
|
ConsoleToMsBuild="true"
|
||||||
|
Condition="'$(RuntimeIdentifier)' == ''"
|
||||||
|
/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
@ -35,6 +35,8 @@ namespace Ryujinx.Common.Configuration
|
|||||||
#pragma warning restore IDE0055
|
#pragma warning restore IDE0055
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static float ToFloatY(this AspectRatio aspectRatio)
|
public static float ToFloatY(this AspectRatio aspectRatio)
|
||||||
{
|
{
|
||||||
|
60
src/Ryujinx.Common/Configuration/DirtyHack.cs
Normal file
60
src/Ryujinx.Common/Configuration/DirtyHack.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using Gommon;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum DirtyHack : byte
|
||||||
|
{
|
||||||
|
Xc2MenuSoftlockFix = 1,
|
||||||
|
ShaderTranslationDelay = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct EnabledDirtyHack(DirtyHack hack, int value)
|
||||||
|
{
|
||||||
|
public DirtyHack Hack => hack;
|
||||||
|
public int Value => value;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ulong Pack() => Raw.PackBitFields(PackedFormat);
|
||||||
|
|
||||||
|
public static EnabledDirtyHack Unpack(ulong packedHack)
|
||||||
|
{
|
||||||
|
var unpackedFields = packedHack.UnpackBitFields(PackedFormat);
|
||||||
|
if (unpackedFields is not [var hack, var value])
|
||||||
|
throw new ArgumentException(nameof(packedHack));
|
||||||
|
|
||||||
|
return new EnabledDirtyHack((DirtyHack)hack, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint[] Raw => [(uint)Hack, (uint)Value.CoerceAtLeast(0)];
|
||||||
|
|
||||||
|
public static readonly byte[] PackedFormat = [8, 32];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DirtyHacks : Dictionary<DirtyHack, int>
|
||||||
|
{
|
||||||
|
public DirtyHacks(IEnumerable<EnabledDirtyHack> hacks)
|
||||||
|
=> hacks.ForEach(edh => Add(edh.Hack, edh.Value));
|
||||||
|
|
||||||
|
public DirtyHacks(ulong[] packedHacks) : this(packedHacks.Select(EnabledDirtyHack.Unpack)) {}
|
||||||
|
|
||||||
|
public ulong[] PackEntries()
|
||||||
|
=> Entries.Select(it => it.Pack()).ToArray();
|
||||||
|
|
||||||
|
public EnabledDirtyHack[] Entries
|
||||||
|
=> this
|
||||||
|
.Select(it => new EnabledDirtyHack(it.Key, it.Value))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
public static implicit operator DirtyHacks(EnabledDirtyHack[] hacks) => new(hacks);
|
||||||
|
public static implicit operator DirtyHacks(ulong[] packedHacks) => new(packedHacks);
|
||||||
|
|
||||||
|
public new int this[DirtyHack hack] => TryGetValue(hack, out var value) ? value : -1;
|
||||||
|
|
||||||
|
public bool IsEnabled(DirtyHack hack) => ContainsKey(hack);
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,9 @@ namespace Ryujinx.Common.Configuration
|
|||||||
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||||
public enum GraphicsBackend
|
public enum GraphicsBackend
|
||||||
{
|
{
|
||||||
|
Auto,
|
||||||
Vulkan,
|
Vulkan,
|
||||||
OpenGl,
|
OpenGl,
|
||||||
|
Metal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
public class JoyconConfigControllerStick<TButton, TStick> where TButton : unmanaged where TStick : unmanaged
|
public class JoyconConfigControllerStick<TButton, TStick>
|
||||||
|
where TButton : unmanaged
|
||||||
|
where TStick : unmanaged
|
||||||
{
|
{
|
||||||
public TStick Joystick { get; set; }
|
public TStick Joystick { get; set; }
|
||||||
public bool InvertStickX { get; set; }
|
public bool InvertStickX { get; set; }
|
||||||
|
@ -2,7 +2,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
{
|
{
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync { get; set; }
|
public Key ToggleVSyncMode { get; set; }
|
||||||
public Key Screenshot { get; set; }
|
public Key Screenshot { get; set; }
|
||||||
public Key ShowUI { get; set; }
|
public Key ShowUI { get; set; }
|
||||||
public Key Pause { get; set; }
|
public Key Pause { get; set; }
|
||||||
@ -11,5 +11,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
public Key ResScaleDown { get; set; }
|
public Key ResScaleDown { get; set; }
|
||||||
public Key VolumeUp { get; set; }
|
public Key VolumeUp { get; set; }
|
||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
|
public Key CustomVSyncIntervalIncrement { get; set; }
|
||||||
|
public Key CustomVSyncIntervalDecrement { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,6 @@ namespace Ryujinx.Common.Configuration
|
|||||||
Bilinear,
|
Bilinear,
|
||||||
Nearest,
|
Nearest,
|
||||||
Fsr,
|
Fsr,
|
||||||
|
Area,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
src/Ryujinx.Common/Configuration/VSyncMode.cs
Normal file
9
src/Ryujinx.Common/Configuration/VSyncMode.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
public enum VSyncMode
|
||||||
|
{
|
||||||
|
Switch,
|
||||||
|
Unbounded,
|
||||||
|
Custom
|
||||||
|
}
|
||||||
|
}
|
@ -8,10 +8,10 @@ namespace Ryujinx.Common
|
|||||||
public static class StreamExtensions
|
public static class StreamExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a <see cref="ReadOnlySpan{int}" /> to this stream.
|
/// Writes an int span to this stream.
|
||||||
///
|
///
|
||||||
/// This default implementation converts each buffer value to a stack-allocated
|
/// This default implementation converts each buffer value to a stack-allocated
|
||||||
/// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
|
/// byte array, then writes it to the Stream using <see cref="Stream.Write(ReadOnlySpan{byte})" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The stream to be written to</param>
|
/// <param name="stream">The stream to be written to</param>
|
||||||
/// <param name="buffer">The buffer of values to be written</param>
|
/// <param name="buffer">The buffer of values to be written</param>
|
||||||
|
@ -3,10 +3,28 @@ using System;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common.Helper
|
namespace Ryujinx.Common.Helper
|
||||||
{
|
{
|
||||||
public static partial class ConsoleHelper
|
public static partial class ConsoleHelper
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[LibraryImport("kernel32")]
|
||||||
|
private static partial nint GetConsoleWindow();
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[LibraryImport("user32")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[LibraryImport("user32")]
|
||||||
|
private static partial nint GetForegroundWindow();
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[LibraryImport("user32")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static partial bool SetForegroundWindow(nint hWnd);
|
||||||
|
|
||||||
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||||
|
|
||||||
public static void SetConsoleWindowState(bool show)
|
public static void SetConsoleWindowState(bool show)
|
||||||
@ -35,16 +53,11 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetForegroundWindow(hWnd);
|
||||||
|
|
||||||
|
hWnd = GetForegroundWindow();
|
||||||
|
|
||||||
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[LibraryImport("kernel32")]
|
|
||||||
private static partial nint GetConsoleWindow();
|
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[LibraryImport("user32")]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ using System.Linq;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common.Helper
|
namespace Ryujinx.Common.Helper
|
||||||
{
|
{
|
||||||
public static partial class FileAssociationHelper
|
public static partial class FileAssociationHelper
|
||||||
{
|
{
|
||||||
@ -23,7 +23,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
[LibraryImport("shell32.dll", SetLastError = true)]
|
[LibraryImport("shell32.dll", SetLastError = true)]
|
||||||
public static partial void SHChangeNotify(uint wEventId, uint uFlags, nint dwItem1, nint dwItem2);
|
public static partial void SHChangeNotify(uint wEventId, uint uFlags, nint dwItem1, nint dwItem2);
|
||||||
|
|
||||||
public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild;
|
public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows());
|
||||||
|
|
||||||
public static bool AreMimeTypesRegistered
|
public static bool AreMimeTypesRegistered
|
||||||
{
|
{
|
||||||
@ -101,13 +101,13 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
{
|
{
|
||||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(@$"Software\Classes\{ext}");
|
RegistryKey key = Registry.CurrentUser.OpenSubKey(@$"Software\Classes\{ext}");
|
||||||
|
|
||||||
if (key is null)
|
var openCmd = key?.OpenSubKey(@"shell\open\command");
|
||||||
|
|
||||||
|
if (openCmd is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var openCmd = key.OpenSubKey(@"shell\open\command");
|
|
||||||
|
|
||||||
string keyValue = (string)openCmd.GetValue(string.Empty);
|
string keyValue = (string)openCmd.GetValue(string.Empty);
|
||||||
|
|
||||||
return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName));
|
return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName));
|
||||||
@ -132,7 +132,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
|
|
||||||
if (uninstall)
|
if (uninstall)
|
||||||
{
|
{
|
||||||
// If the types don't already exist, there's nothing to do and we can call this operation successful.
|
// If the types don't already exist, there's nothing to do, and we can call this operation successful.
|
||||||
if (!AreMimeTypesRegisteredWindows())
|
if (!AreMimeTypesRegisteredWindows())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
@ -3,7 +3,7 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common.Helper
|
namespace Ryujinx.Common.Helper
|
||||||
{
|
{
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
public static class LinuxHelper
|
public static class LinuxHelper
|
@ -2,7 +2,7 @@ using System;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common.Helper
|
namespace Ryujinx.Common.Helper
|
||||||
{
|
{
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
public static partial class ObjectiveC
|
public static partial class ObjectiveC
|
@ -1,10 +1,11 @@
|
|||||||
|
using Gommon;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common.Helper
|
namespace Ryujinx.Common.Helper
|
||||||
{
|
{
|
||||||
public static partial class OpenHelper
|
public static partial class OpenHelper
|
||||||
{
|
{
|
||||||
@ -34,6 +35,8 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void OpenFolder(FilePath path) => OpenFolder(path.Path);
|
||||||
|
|
||||||
public static void LocateFile(string path)
|
public static void LocateFile(string path)
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@ -157,21 +158,16 @@ namespace Ryujinx.Common.Logging
|
|||||||
_time.Restart();
|
_time.Restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ILogTarget GetTarget(string targetName)
|
private static ILogTarget GetTarget(string targetName)
|
||||||
{
|
=> _logTargets.FirstOrDefault(target => target.Name.Equals(targetName));
|
||||||
foreach (var target in _logTargets)
|
|
||||||
{
|
|
||||||
if (target.Name.Equals(targetName))
|
|
||||||
{
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddTarget(ILogTarget target)
|
public static void AddTarget(ILogTarget target)
|
||||||
{
|
{
|
||||||
|
if (_logTargets.Any(t => t.Name == target.Name))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_logTargets.Add(target);
|
_logTargets.Add(target);
|
||||||
|
|
||||||
Updated += target.Log;
|
Updated += target.Log;
|
||||||
|
@ -27,11 +27,7 @@ namespace Ryujinx.Common.Logging.Targets
|
|||||||
|
|
||||||
private readonly int _overflowTimeout;
|
private readonly int _overflowTimeout;
|
||||||
|
|
||||||
string ILogTarget.Name { get => _target.Name; }
|
string ILogTarget.Name => _target.Name;
|
||||||
|
|
||||||
public AsyncLogTargetWrapper(ILogTarget target)
|
|
||||||
: this(target, -1)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
|
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
|
||||||
{
|
{
|
||||||
|
@ -69,9 +69,10 @@ namespace Ryujinx.Common.Logging.Targets
|
|||||||
}
|
}
|
||||||
|
|
||||||
string version = ReleaseInformation.Version;
|
string version = ReleaseInformation.Version;
|
||||||
|
string appName = ReleaseInformation.IsCanaryBuild ? "Ryujinx_Canary" : "Ryujinx";
|
||||||
|
|
||||||
// Get path for the current time
|
// Get path for the current time
|
||||||
path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log");
|
path = Path.Combine(logDir.FullName, $"{appName}_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -124,7 +124,7 @@ namespace Ryujinx.Common.PreciseSleep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
private readonly List<NanosleepThread> _threads = new();
|
private readonly List<NanosleepThread> _threads = new();
|
||||||
private readonly List<NanosleepThread> _active = new();
|
private readonly List<NanosleepThread> _active = new();
|
||||||
private readonly Stack<NanosleepThread> _free = new();
|
private readonly Stack<NanosleepThread> _free = new();
|
||||||
|
@ -50,7 +50,7 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
private long _lastTicks = PerformanceCounter.ElapsedTicks;
|
private long _lastTicks = PerformanceCounter.ElapsedTicks;
|
||||||
private long _lastId;
|
private long _lastId;
|
||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly Lock _lock = new();
|
||||||
private readonly List<WaitingObject> _waitingObjects = new();
|
private readonly List<WaitingObject> _waitingObjects = new();
|
||||||
|
|
||||||
private WindowsGranularTimer()
|
private WindowsGranularTimer()
|
||||||
|
@ -53,6 +53,9 @@ namespace Ryujinx.Common
|
|||||||
{
|
{
|
||||||
public static void LogValueChange<T>(LogClass logClass, ReactiveEventArgs<T> eventArgs, string valueName)
|
public static void LogValueChange<T>(LogClass logClass, ReactiveEventArgs<T> eventArgs, string valueName)
|
||||||
{
|
{
|
||||||
|
if (eventArgs.AreValuesEqual)
|
||||||
|
return;
|
||||||
|
|
||||||
string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}");
|
string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}");
|
||||||
|
|
||||||
Logger.Info?.Print(logClass, message);
|
Logger.Info?.Print(logClass, message);
|
||||||
@ -65,5 +68,22 @@ namespace Ryujinx.Common
|
|||||||
{
|
{
|
||||||
public T OldValue { get; } = oldValue;
|
public T OldValue { get; } = oldValue;
|
||||||
public T NewValue { get; } = newValue;
|
public T NewValue { get; } = newValue;
|
||||||
|
|
||||||
|
public bool AreValuesEqual
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (OldValue == null && NewValue == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (OldValue == null && NewValue != null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (OldValue != null && NewValue == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return OldValue!.Equals(NewValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
@ -5,7 +6,6 @@ namespace Ryujinx.Common
|
|||||||
// DO NOT EDIT, filled by CI
|
// DO NOT EDIT, filled by CI
|
||||||
public static class ReleaseInformation
|
public static class ReleaseInformation
|
||||||
{
|
{
|
||||||
private const string FlatHubChannel = "flathub";
|
|
||||||
private const string CanaryChannel = "canary";
|
private const string CanaryChannel = "canary";
|
||||||
private const string ReleaseChannel = "release";
|
private const string ReleaseChannel = "release";
|
||||||
|
|
||||||
@ -28,12 +28,18 @@ namespace Ryujinx.Common
|
|||||||
!ReleaseChannelRepo.StartsWith("%%") &&
|
!ReleaseChannelRepo.StartsWith("%%") &&
|
||||||
!ConfigFileName.StartsWith("%%");
|
!ConfigFileName.StartsWith("%%");
|
||||||
|
|
||||||
public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannel);
|
|
||||||
|
|
||||||
public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel);
|
public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel);
|
||||||
|
|
||||||
public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);
|
public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);
|
||||||
|
|
||||||
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||||
|
|
||||||
|
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
||||||
|
IsCanaryBuild
|
||||||
|
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
||||||
|
: $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}";
|
||||||
|
|
||||||
|
public static string GetChangelogForVersion(Version version) =>
|
||||||
|
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,129 +1,51 @@
|
|||||||
using DiscordRPC;
|
using Gommon;
|
||||||
using Humanizer;
|
using Ryujinx.Common.Configuration;
|
||||||
using Humanizer.Localisation;
|
using System;
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.HLE.Loaders.Processes;
|
|
||||||
using Ryujinx.UI.App.Common;
|
|
||||||
using Ryujinx.UI.Common.Configuration;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common
|
namespace Ryujinx.Common
|
||||||
{
|
{
|
||||||
public static class DiscordIntegrationModule
|
public static class TitleIDs
|
||||||
{
|
{
|
||||||
public static Timestamps StartedAt { get; set; }
|
public static ReactiveObject<Optional<string>> CurrentApplication { get; set; } = new();
|
||||||
|
|
||||||
private static string VersionString
|
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
|
||||||
=> (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}";
|
|
||||||
|
|
||||||
private static readonly string _description =
|
|
||||||
ReleaseInformation.IsValid
|
|
||||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
|
|
||||||
: "dev build";
|
|
||||||
|
|
||||||
private const string ApplicationId = "1293250299716173864";
|
|
||||||
|
|
||||||
private const int ApplicationByteLimit = 128;
|
|
||||||
private const string Ellipsis = "…";
|
|
||||||
|
|
||||||
private static DiscordRpcClient _discordClient;
|
|
||||||
private static RichPresence _discordPresenceMain;
|
|
||||||
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
{
|
||||||
_discordPresenceMain = new RichPresence
|
switch (currentBackend)
|
||||||
{
|
{
|
||||||
Assets = new Assets
|
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS():
|
||||||
{
|
return GraphicsBackend.Vulkan;
|
||||||
LargeImageKey = "ryujinx",
|
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal:
|
||||||
LargeImageText = TruncateToByteLength(_description)
|
return currentBackend;
|
||||||
},
|
|
||||||
Details = "Main Menu",
|
|
||||||
State = "Idling",
|
|
||||||
Timestamps = StartedAt
|
|
||||||
};
|
|
||||||
|
|
||||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
|
||||||
{
|
|
||||||
if (evnt.OldValue != evnt.NewValue)
|
|
||||||
{
|
|
||||||
// If the integration was active, disable it and unload everything
|
|
||||||
if (evnt.OldValue)
|
|
||||||
{
|
|
||||||
_discordClient?.Dispose();
|
|
||||||
|
|
||||||
_discordClient = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we need to activate it and the client isn't active, initialize it
|
|
||||||
if (evnt.NewValue && _discordClient == null)
|
|
||||||
{
|
|
||||||
_discordClient = new DiscordRpcClient(ApplicationId);
|
|
||||||
|
|
||||||
_discordClient.Initialize();
|
|
||||||
_discordClient.SetPresence(_discordPresenceMain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
|
||||||
{
|
|
||||||
_discordClient?.SetPresence(new RichPresence
|
|
||||||
{
|
|
||||||
Assets = new Assets
|
|
||||||
{
|
|
||||||
LargeImageKey = _discordGameAssetKeys.Contains(procRes.ProgramIdText) ? procRes.ProgramIdText : "game",
|
|
||||||
LargeImageText = TruncateToByteLength($"{appMeta.Title} (v{procRes.DisplayVersion})"),
|
|
||||||
SmallImageKey = "ryujinx",
|
|
||||||
SmallImageText = TruncateToByteLength(_description)
|
|
||||||
},
|
|
||||||
Details = TruncateToByteLength($"Playing {appMeta.Title}"),
|
|
||||||
State = appMeta.LastPlayed.HasValue && appMeta.TimePlayed.TotalSeconds > 5
|
|
||||||
? $"Total play time: {appMeta.TimePlayed.Humanize(2, false, maxUnit: TimeUnit.Hour)}"
|
|
||||||
: "Never played",
|
|
||||||
Timestamps = Timestamps.Now
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain);
|
|
||||||
|
|
||||||
private static string TruncateToByteLength(string input)
|
|
||||||
{
|
|
||||||
if (Encoding.UTF8.GetByteCount(input) <= ApplicationByteLimit)
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the length to trim the string to guarantee we have space for the trailing ellipsis.
|
if (!(OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture is Architecture.Arm64))
|
||||||
int trimLimit = ApplicationByteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
|
return GraphicsBackend.Vulkan;
|
||||||
|
|
||||||
// Make sure the string is long enough to perform the basic trim.
|
return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan;
|
||||||
// Amount of bytes != Length of the string
|
|
||||||
if (input.Length > trimLimit)
|
|
||||||
{
|
|
||||||
// Basic trim to best case scenario of 1 byte characters.
|
|
||||||
input = input[..trimLimit];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
|
|
||||||
{
|
|
||||||
// Remove one character from the end of the string at a time.
|
|
||||||
input = input[..^1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return input.TrimEnd() + Ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly string[] GreatMetalTitles =
|
||||||
|
[
|
||||||
|
"01006f8002326000", // Animal Crossings: New Horizons
|
||||||
|
"01009bf0072d4000", // Captain Toad: Treasure Tracker
|
||||||
|
"0100a5c00d162000", // Cuphead
|
||||||
|
"010023800d64a000", // Deltarune
|
||||||
|
"010028600EBDA000", // Mario 3D World
|
||||||
|
"0100152000022000", // Mario Kart 8 Deluxe
|
||||||
|
"01005CA01580E000", // Persona 5
|
||||||
|
"0100187003A36000", // Pokémon: Let's Go, Evoli!
|
||||||
|
"010003f003a34000", // Pokémon: Let's Go, Pikachu!
|
||||||
|
"01008C0016544000", // Sea of Stars
|
||||||
|
"01006A800016E000", // Smash Ultimate
|
||||||
|
"0100000000010000", // Super Mario Odyessy
|
||||||
|
];
|
||||||
|
|
||||||
|
public static string GetDiscordGameAsset(string titleId)
|
||||||
|
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
|
||||||
|
|
||||||
public static void Exit()
|
public static readonly string[] DiscordGameAssetKeys =
|
||||||
{
|
|
||||||
_discordClient?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly string[] _discordGameAssetKeys =
|
|
||||||
[
|
[
|
||||||
"010055d009f78000", // Fire Emblem: Three Houses
|
"010055d009f78000", // Fire Emblem: Three Houses
|
||||||
"0100a12011cc8000", // Fire Emblem: Shadow Dragon
|
"0100a12011cc8000", // Fire Emblem: Shadow Dragon
|
||||||
@ -247,6 +169,7 @@ namespace Ryujinx.UI.Common
|
|||||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||||
"0100744001588000", // Cars 3: Driven to Win
|
"0100744001588000", // Cars 3: Driven to Win
|
||||||
"0100b41013c82000", // Cruis'n Blast
|
"0100b41013c82000", // Cruis'n Blast
|
||||||
|
"01001b300b9be000", // Diablo III: Eternal Collection
|
||||||
"01008c8012920000", // Dying Light Platinum Edition
|
"01008c8012920000", // Dying Light Platinum Edition
|
||||||
"010073c01af34000", // LEGO Horizon Adventures
|
"010073c01af34000", // LEGO Horizon Adventures
|
||||||
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
||||||
@ -264,6 +187,7 @@ namespace Ryujinx.UI.Common
|
|||||||
"0100800015926000", // Suika Game
|
"0100800015926000", // Suika Game
|
||||||
"0100e46006708000", // Terraria
|
"0100e46006708000", // Terraria
|
||||||
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
||||||
|
"010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon-
|
||||||
"010080b00ad66000", // Undertale
|
"010080b00ad66000", // Undertale
|
||||||
];
|
];
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Ryujinx.UI.Common
|
namespace Ryujinx.Common.UI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represent a common error that could be reported to the user by the emulator.
|
/// Represent a common error that could be reported to the user by the emulator.
|
@ -40,5 +40,35 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
return (value >> 32) | (value << 32);
|
return (value >> 32) | (value << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Never actually written bit packing logic before, so I looked it up.
|
||||||
|
// This code is from https://gist.github.com/Alan-FGR/04938e93e2bffdf5802ceb218a37c195
|
||||||
|
|
||||||
|
public static ulong PackBitFields(this uint[] values, byte[] bitFields)
|
||||||
|
{
|
||||||
|
ulong retVal = values[0]; //we set the first value right away
|
||||||
|
for (int f = 1; f < values.Length; f++)
|
||||||
|
{
|
||||||
|
retVal <<= bitFields[f]; // we shift the previous value
|
||||||
|
retVal += values[f];// and add our current value
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint[] UnpackBitFields(this ulong packed, byte[] bitFields)
|
||||||
|
{
|
||||||
|
int fields = bitFields.Length - 1; // number of fields to unpack
|
||||||
|
uint[] retArr = new uint[fields + 1]; // init return array
|
||||||
|
int curPos = 0; // current field bit position (start)
|
||||||
|
int lastEnd; // position where last field ended
|
||||||
|
for (int f = fields; f >= 0; f--) // loop from last
|
||||||
|
{
|
||||||
|
lastEnd = curPos; // we store where the last value ended
|
||||||
|
curPos += bitFields[f]; // we get where the current value starts
|
||||||
|
int leftShift = 64 - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion
|
||||||
|
retArr[f] = (uint)((packed << leftShift) >> leftShift + lastEnd); // we do magic
|
||||||
|
}
|
||||||
|
return retArr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -27,9 +28,14 @@ namespace Ryujinx.Common.Utilities
|
|||||||
ReadCommentHandling = JsonCommentHandling.Skip
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Serialize(value, typeInfo);
|
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
=> JsonSerializer.Serialize(value, typeInfo);
|
||||||
|
|
||||||
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Deserialize(value, typeInfo);
|
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
|
||||||
|
=> JsonSerializer.Deserialize(value, typeInfo);
|
||||||
|
|
||||||
|
public static T Deserialize<T>(ReadOnlySpan<byte> utf8Value, JsonTypeInfo<T> typeInfo)
|
||||||
|
=> JsonSerializer.Deserialize<T>(utf8Value, typeInfo);
|
||||||
|
|
||||||
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
||||||
{
|
{
|
||||||
|
482
src/Ryujinx.Cpu/AddressTable.cs
Normal file
482
src/Ryujinx.Cpu/AddressTable.cs
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
using ARMeilleure.Memory;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Cpu.Signal;
|
||||||
|
using Ryujinx.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using static Ryujinx.Cpu.MemoryEhMeilleure;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a table of guest address to a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||||
|
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a page of the address table.
|
||||||
|
/// </summary>
|
||||||
|
private readonly struct AddressTablePage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// True if the allocation belongs to a sparse block, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsSparse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base address for the page.
|
||||||
|
/// </summary>
|
||||||
|
public readonly IntPtr Address;
|
||||||
|
|
||||||
|
public AddressTablePage(bool isSparse, IntPtr address)
|
||||||
|
{
|
||||||
|
IsSparse = isSparse;
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
|
||||||
|
/// </summary>
|
||||||
|
private readonly struct TableSparseBlock : IDisposable
|
||||||
|
{
|
||||||
|
public readonly SparseMemoryBlock Block;
|
||||||
|
private readonly TrackingEventDelegate _trackingEvent;
|
||||||
|
|
||||||
|
public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
|
||||||
|
{
|
||||||
|
var block = new SparseMemoryBlock(size, pageInit, null);
|
||||||
|
|
||||||
|
_trackingEvent = (ulong address, ulong size, bool write) =>
|
||||||
|
{
|
||||||
|
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||||
|
ensureMapped((IntPtr)pointer);
|
||||||
|
return pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||||
|
(nuint)block.Block.Pointer,
|
||||||
|
(nuint)(block.Block.Pointer + (IntPtr)block.Block.Size),
|
||||||
|
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||||
|
|
||||||
|
if (!added)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
|
||||||
|
|
||||||
|
Block.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
private TEntry** _table;
|
||||||
|
private readonly List<AddressTablePage> _pages;
|
||||||
|
private TEntry _fill;
|
||||||
|
|
||||||
|
private readonly MemoryBlock _sparseFill;
|
||||||
|
private readonly SparseMemoryBlock _fillBottomLevel;
|
||||||
|
private readonly TEntry* _fillBottomLevelPtr;
|
||||||
|
|
||||||
|
private readonly List<TableSparseBlock> _sparseReserved;
|
||||||
|
private readonly ReaderWriterLockSlim _sparseLock;
|
||||||
|
|
||||||
|
private ulong _sparseBlockSize;
|
||||||
|
private ulong _sparseReservedOffset;
|
||||||
|
|
||||||
|
public bool Sparse { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ulong Mask { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AddressTableLevel[] Levels { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public TEntry Fill
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _fill;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
UpdateFill(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IntPtr Base
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
lock (_pages)
|
||||||
|
{
|
||||||
|
return (IntPtr)GetRootPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
||||||
|
/// <see cref="Level"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="levels">Levels for the address table</param>
|
||||||
|
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
||||||
|
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||||
|
public AddressTable(AddressTableLevel[] levels, bool sparse)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(levels);
|
||||||
|
|
||||||
|
_pages = new List<AddressTablePage>(capacity: 16);
|
||||||
|
|
||||||
|
Levels = levels;
|
||||||
|
Mask = 0;
|
||||||
|
|
||||||
|
foreach (var level in Levels)
|
||||||
|
{
|
||||||
|
Mask |= level.Mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sparse = sparse;
|
||||||
|
|
||||||
|
if (sparse)
|
||||||
|
{
|
||||||
|
// If the address table is sparse, allocate a fill block
|
||||||
|
|
||||||
|
_sparseFill = new MemoryBlock(268435456ul, MemoryAllocationFlags.Mirrorable); //low Power TC uses size: 65536ul
|
||||||
|
|
||||||
|
ulong bottomLevelSize = (1ul << levels.Last().Length) * (ulong)sizeof(TEntry);
|
||||||
|
|
||||||
|
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
||||||
|
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
||||||
|
|
||||||
|
_sparseReserved = new List<TableSparseBlock>();
|
||||||
|
_sparseLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
_sparseBlockSize = bottomLevelSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an <see cref="AddressTable{TEntry}"/> instance for an ARM function table.
|
||||||
|
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
|
||||||
|
/// <param name="type">Memory manager type</param>
|
||||||
|
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
|
||||||
|
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type)
|
||||||
|
{
|
||||||
|
// Assume software memory means that we don't want to use any signal handlers.
|
||||||
|
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable;
|
||||||
|
|
||||||
|
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the fill value for the bottom level of the table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fillValue">New fill value</param>
|
||||||
|
private void UpdateFill(TEntry fillValue)
|
||||||
|
{
|
||||||
|
if (_sparseFill != null)
|
||||||
|
{
|
||||||
|
Span<byte> span = _sparseFill.GetSpan(0, (int)_sparseFill.Size);
|
||||||
|
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fill = fillValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signal that the given code range exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address"></param>
|
||||||
|
/// <param name="size"></param>
|
||||||
|
public void SignalCodeRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
AddressTableLevel bottom = Levels.Last();
|
||||||
|
ulong bottomLevelEntries = 1ul << bottom.Length;
|
||||||
|
|
||||||
|
ulong entryIndex = address >> bottom.Index;
|
||||||
|
ulong entries = size >> bottom.Index;
|
||||||
|
entries += entryIndex - BitUtils.AlignDown(entryIndex, bottomLevelEntries);
|
||||||
|
|
||||||
|
_sparseBlockSize = Math.Max(_sparseBlockSize, BitUtils.AlignUp(entries, bottomLevelEntries) * (ulong)sizeof(TEntry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsValid(ulong address)
|
||||||
|
{
|
||||||
|
return (address & ~Mask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ref TEntry GetValue(ulong address)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (!IsValid(address))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_pages)
|
||||||
|
{
|
||||||
|
TEntry* page = GetPage(address);
|
||||||
|
|
||||||
|
long index = Levels[^1].GetValue(address);
|
||||||
|
|
||||||
|
EnsureMapped((IntPtr)(page + index));
|
||||||
|
|
||||||
|
return ref page[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the leaf page for the specified guest <paramref name="address"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
|
||||||
|
private TEntry* GetPage(ulong address)
|
||||||
|
{
|
||||||
|
TEntry** page = GetRootPage();
|
||||||
|
|
||||||
|
for (int i = 0; i < Levels.Length - 1; i++)
|
||||||
|
{
|
||||||
|
ref AddressTableLevel level = ref Levels[i];
|
||||||
|
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||||
|
|
||||||
|
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
||||||
|
{
|
||||||
|
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
||||||
|
|
||||||
|
if (i == Levels.Length - 2)
|
||||||
|
{
|
||||||
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page = (TEntry**)nextPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (TEntry*)page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">Pointer to be mapped</param>
|
||||||
|
private void EnsureMapped(IntPtr ptr)
|
||||||
|
{
|
||||||
|
if (Sparse)
|
||||||
|
{
|
||||||
|
// Check sparse allocations to see if the pointer is in any of them.
|
||||||
|
// Ensure the page is committed if there's a match.
|
||||||
|
|
||||||
|
_sparseLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (TableSparseBlock reserved in _sparseReserved)
|
||||||
|
{
|
||||||
|
SparseMemoryBlock sparse = reserved.Block;
|
||||||
|
|
||||||
|
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||||
|
{
|
||||||
|
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sparseLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the fill value for a non-leaf level of the table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level">Level to get the fill value for</param>
|
||||||
|
/// <returns>The fill value</returns>
|
||||||
|
private IntPtr GetFillValue(int level)
|
||||||
|
{
|
||||||
|
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||||
|
{
|
||||||
|
return (IntPtr)_fillBottomLevelPtr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
|
||||||
|
private TEntry** GetRootPage()
|
||||||
|
{
|
||||||
|
if (_table == null)
|
||||||
|
{
|
||||||
|
if (Levels.Length == 1)
|
||||||
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true);
|
||||||
|
else
|
||||||
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a leaf page with the fill value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="page">Page to initialize</param>
|
||||||
|
private void InitLeafPage(Span<byte> page)
|
||||||
|
{
|
||||||
|
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserve a new sparse block, and add it to the list.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new sparse block that was added</returns>
|
||||||
|
private TableSparseBlock ReserveNewSparseBlock()
|
||||||
|
{
|
||||||
|
var block = new TableSparseBlock(_sparseBlockSize, EnsureMapped, InitLeafPage);
|
||||||
|
|
||||||
|
_sparseReserved.Add(block);
|
||||||
|
_sparseReservedOffset = 0;
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocates a block of memory of the specified type and length.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of elements</typeparam>
|
||||||
|
/// <param name="length">Number of elements</param>
|
||||||
|
/// <param name="fill">Fill value</param>
|
||||||
|
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||||
|
/// <returns>Allocated block</returns>
|
||||||
|
private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||||
|
{
|
||||||
|
var size = sizeof(T) * length;
|
||||||
|
|
||||||
|
AddressTablePage page;
|
||||||
|
|
||||||
|
if (Sparse && leaf)
|
||||||
|
{
|
||||||
|
_sparseLock.EnterWriteLock();
|
||||||
|
|
||||||
|
SparseMemoryBlock block;
|
||||||
|
|
||||||
|
if (_sparseReserved.Count == 0)
|
||||||
|
{
|
||||||
|
block = ReserveNewSparseBlock().Block;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block = _sparseReserved.Last().Block;
|
||||||
|
|
||||||
|
if (_sparseReservedOffset == block.Block.Size)
|
||||||
|
{
|
||||||
|
block = ReserveNewSparseBlock().Block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||||
|
|
||||||
|
_sparseReservedOffset += (ulong)size;
|
||||||
|
|
||||||
|
_sparseLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
|
||||||
|
page = new AddressTablePage(false, address);
|
||||||
|
|
||||||
|
var span = new Span<T>((void*)page.Address, length);
|
||||||
|
span.Fill(fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pages.Add(page);
|
||||||
|
|
||||||
|
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||||
|
|
||||||
|
return page.Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
|
||||||
|
/// instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
foreach (var page in _pages)
|
||||||
|
{
|
||||||
|
if (!page.IsSparse)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(page.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sparse)
|
||||||
|
{
|
||||||
|
foreach (TableSparseBlock block in _sparseReserved)
|
||||||
|
{
|
||||||
|
block.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sparseReserved.Clear();
|
||||||
|
|
||||||
|
_fillBottomLevel.Dispose();
|
||||||
|
_sparseFill.Dispose();
|
||||||
|
_sparseLock.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
~AddressTable()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
|
@ -230,25 +230,20 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<HostMemoryRange>();
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||||
if (guestRegions == null)
|
if (guestRegions == null)
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var regions = new HostMemoryRange[guestRegions.Count];
|
foreach (var guestRegion in guestRegions)
|
||||||
|
|
||||||
for (int i = 0; i < regions.Length; i++)
|
|
||||||
{
|
{
|
||||||
var guestRegion = guestRegions[i];
|
|
||||||
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return regions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -256,23 +251,24 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<MemoryRange>();
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetPhysicalRegionsImpl(va, size);
|
foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size))
|
||||||
|
{
|
||||||
|
yield return physicalRegion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|
||||||
@ -280,14 +276,14 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
if (!ValidateAddress(va + PageSize))
|
if (!ValidateAddress(va + PageSize))
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||||
|
|
||||||
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||||
{
|
{
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
yield return new MemoryRange(regionStart, regionSize);
|
||||||
regionStart = newPa;
|
regionStart = newPa;
|
||||||
regionSize = 0;
|
regionSize = 0;
|
||||||
}
|
}
|
||||||
@ -296,9 +292,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
regionSize += PageSize;
|
regionSize += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
yield return new MemoryRange(regionStart, regionSize);
|
||||||
|
|
||||||
return regions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
@ -12,7 +13,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
private static int _addressSpaces;
|
private static int _addressSpaces;
|
||||||
private static HvIpaAllocator _ipaAllocator;
|
private static HvIpaAllocator _ipaAllocator;
|
||||||
private static readonly object _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
|
|
||||||
public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block)
|
public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block)
|
||||||
{
|
{
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Cpu
|
|||||||
/// <param name="displayVersion">Version of the application</param>
|
/// <param name="displayVersion">Version of the application</param>
|
||||||
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
||||||
/// <returns>Disk cache load progress reporter and manager</returns>
|
/// <returns>Disk cache load progress reporter and manager</returns>
|
||||||
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled);
|
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
||||||
|
@ -115,6 +115,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly AddressIntrusiveRedBlackTree<Mapping> _mappingTree;
|
private readonly AddressIntrusiveRedBlackTree<Mapping> _mappingTree;
|
||||||
|
|
||||||
|
// type is not Lock due to the unique usage of this mechanism,
|
||||||
|
// an arbitrary object is used as the lock passed in by constructor.
|
||||||
private readonly object _lock;
|
private readonly object _lock;
|
||||||
|
|
||||||
public Block(MemoryTracking tracking, Func<ulong, ulong> readPtCallback, MemoryBlock memory, ulong size, object locker) : base(memory, size)
|
public Block(MemoryTracking tracking, Func<ulong, ulong> readPtCallback, MemoryBlock memory, ulong size, object locker) : base(memory, size)
|
||||||
@ -174,6 +177,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked
|
|||||||
|
|
||||||
private readonly MemoryTracking _tracking;
|
private readonly MemoryTracking _tracking;
|
||||||
private readonly Func<ulong, ulong> _readPtCallback;
|
private readonly Func<ulong, ulong> _readPtCallback;
|
||||||
|
|
||||||
|
// type is not Lock due to the unique usage of this mechanism,
|
||||||
|
// an arbitrary object is used as the lock passed in by constructor.
|
||||||
private readonly object _lock;
|
private readonly object _lock;
|
||||||
|
|
||||||
public AddressSpacePartitionAllocator(
|
public AddressSpacePartitionAllocator(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Cpu.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
@ -9,11 +10,13 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
|
private readonly AddressTable<ulong> _functionTable;
|
||||||
|
|
||||||
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||||
{
|
{
|
||||||
_tickSource = tickSource;
|
_tickSource = tickSource;
|
||||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit);
|
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||||
|
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
|
||||||
|
|
||||||
if (memory.Type.IsHostMappedOrTracked())
|
if (memory.Type.IsHostMappedOrTracked())
|
||||||
{
|
{
|
||||||
@ -47,14 +50,15 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled));
|
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
|
_functionTable.SignalCodeRange(address, size);
|
||||||
_translator.PrepareCodeRange(address, size);
|
_translator.PrepareCodeRange(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,25 +250,20 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<HostMemoryRange>();
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||||
if (guestRegions == null)
|
if (guestRegions == null)
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var regions = new HostMemoryRange[guestRegions.Count];
|
foreach (var guestRegion in guestRegions)
|
||||||
|
|
||||||
for (int i = 0; i < regions.Length; i++)
|
|
||||||
{
|
{
|
||||||
var guestRegion = guestRegions[i];
|
|
||||||
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return regions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -276,23 +271,24 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<MemoryRange>();
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetPhysicalRegionsImpl(va, size);
|
foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size))
|
||||||
|
{
|
||||||
|
yield return physicalRegion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|
||||||
@ -300,14 +296,14 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
if (!ValidateAddress(va + PageSize))
|
if (!ValidateAddress(va + PageSize))
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||||
|
|
||||||
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||||
{
|
{
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
yield return new MemoryRange(regionStart, regionSize);
|
||||||
regionStart = newPa;
|
regionStart = newPa;
|
||||||
regionSize = 0;
|
regionSize = 0;
|
||||||
}
|
}
|
||||||
@ -316,9 +312,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
regionSize += PageSize;
|
regionSize += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
yield return new MemoryRange(regionStart, regionSize);
|
||||||
|
|
||||||
return regions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -475,17 +475,15 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
return GetPhysicalRegionsImpl(va, size);
|
return GetPhysicalRegionsImpl(va, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|
||||||
@ -493,14 +491,14 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
if (!ValidateAddress(va + PageSize))
|
if (!ValidateAddress(va + PageSize))
|
||||||
{
|
{
|
||||||
return null;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||||
|
|
||||||
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||||
{
|
{
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
yield return new MemoryRange(regionStart, regionSize);
|
||||||
regionStart = newPa;
|
regionStart = newPa;
|
||||||
regionSize = 0;
|
regionSize = 0;
|
||||||
}
|
}
|
||||||
@ -509,9 +507,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
regionSize += PageSize;
|
regionSize += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
yield return new MemoryRange(regionStart, regionSize);
|
||||||
|
|
||||||
return regions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -140,6 +140,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
bool isTail = false)
|
bool isTail = false)
|
||||||
{
|
{
|
||||||
int tempRegister;
|
int tempRegister;
|
||||||
|
int tempGuestAddress = -1;
|
||||||
|
|
||||||
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||||
|
funcTable is { Sparse: true };
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@ -153,9 +157,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||||
|
|
||||||
|
if (inlineLookup && guestAddress.Value == 0)
|
||||||
|
{
|
||||||
|
// X0 will be overwritten. Move the address to a temp register.
|
||||||
|
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
||||||
|
asm.Mov(Register(tempGuestAddress), guestAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
||||||
|
|
||||||
if (!isTail)
|
if (!isTail)
|
||||||
{
|
{
|
||||||
@ -176,6 +187,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||||
}
|
}
|
||||||
|
else if (inlineLookup)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
|
||||||
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
guestAddress = Register(tempGuestAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong tableBase = (ulong)funcTable.Base;
|
||||||
|
|
||||||
|
// Index into the table.
|
||||||
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||||
|
{
|
||||||
|
var level = funcTable.Levels[i];
|
||||||
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
|
|
||||||
|
// Index into the page.
|
||||||
|
asm.Add(rn, rn, indexReg);
|
||||||
|
|
||||||
|
// Load the page address.
|
||||||
|
asm.LdrRiUn(rn, rn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.Mov(rn, (ulong)funcPtr);
|
asm.Mov(rn, (ulong)funcPtr);
|
||||||
@ -252,5 +297,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
{
|
{
|
||||||
return new Operand(register, RegisterType.Integer, type);
|
return new Operand(register, RegisterType.Integer, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand Const(long value, OperandType type = OperandType.I64)
|
||||||
|
{
|
||||||
|
return new Operand(type, (ulong)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int NextFreeRegister(int start, int avoid)
|
||||||
|
{
|
||||||
|
if (start == avoid)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,7 +478,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
bool skipContext,
|
bool skipContext,
|
||||||
int spillBaseOffset,
|
int spillBaseOffset,
|
||||||
int? resultRegister,
|
int? resultRegister,
|
||||||
params ulong[] callArgs)
|
params ReadOnlySpan<ulong> callArgs)
|
||||||
{
|
{
|
||||||
uint resultMask = 0u;
|
uint resultMask = 0u;
|
||||||
|
|
||||||
|
@ -305,6 +305,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
bool isTail = false)
|
bool isTail = false)
|
||||||
{
|
{
|
||||||
int tempRegister;
|
int tempRegister;
|
||||||
|
int tempGuestAddress = -1;
|
||||||
|
|
||||||
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||||
|
funcTable is { Sparse: true };
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@ -318,9 +322,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||||
|
|
||||||
|
if (inlineLookup && guestAddress.Value == 0)
|
||||||
|
{
|
||||||
|
// X0 will be overwritten. Move the address to a temp register.
|
||||||
|
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
||||||
|
asm.Mov(Register(tempGuestAddress), guestAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
||||||
|
|
||||||
if (!isTail)
|
if (!isTail)
|
||||||
{
|
{
|
||||||
@ -341,6 +352,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||||
}
|
}
|
||||||
|
else if (inlineLookup)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
|
||||||
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
guestAddress = Register(tempGuestAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong tableBase = (ulong)funcTable.Base;
|
||||||
|
|
||||||
|
// Index into the table.
|
||||||
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||||
|
{
|
||||||
|
var level = funcTable.Levels[i];
|
||||||
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
|
|
||||||
|
// Index into the page.
|
||||||
|
asm.Add(rn, rn, indexReg);
|
||||||
|
|
||||||
|
// Load the page address.
|
||||||
|
asm.LdrRiUn(rn, rn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.Mov(rn, (ulong)funcPtr);
|
asm.Mov(rn, (ulong)funcPtr);
|
||||||
@ -372,7 +417,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
nint funcPtr,
|
nint funcPtr,
|
||||||
int spillBaseOffset,
|
int spillBaseOffset,
|
||||||
int? resultRegister,
|
int? resultRegister,
|
||||||
params ulong[] callArgs)
|
params ReadOnlySpan<ulong> callArgs)
|
||||||
{
|
{
|
||||||
uint resultMask = 0u;
|
uint resultMask = 0u;
|
||||||
|
|
||||||
@ -613,5 +658,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
{
|
{
|
||||||
return new Operand(register, RegisterType.Integer, type);
|
return new Operand(register, RegisterType.Integer, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand Const(long value, OperandType type = OperandType.I64)
|
||||||
|
{
|
||||||
|
return new Operand(type, (ulong)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int NextFreeRegister(int start, int avoid)
|
||||||
|
{
|
||||||
|
if (start == avoid)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||||
{
|
{
|
||||||
@ -23,7 +24,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = new();
|
private static readonly List<CacheEntry> _cacheEntries = new();
|
||||||
|
|
||||||
private static readonly object _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
|
@ -4,6 +4,7 @@ using Ryujinx.Memory;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||||
{
|
{
|
||||||
@ -104,7 +105,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
private readonly MemoryCache _sharedCache;
|
private readonly MemoryCache _sharedCache;
|
||||||
private readonly MemoryCache _localCache;
|
private readonly MemoryCache _localCache;
|
||||||
private readonly PageAlignedRangeList _pendingMap;
|
private readonly PageAlignedRangeList _pendingMap;
|
||||||
private readonly object _lock;
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
class ThreadLocalCacheEntry
|
class ThreadLocalCacheEntry
|
||||||
{
|
{
|
||||||
@ -137,7 +138,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
_sharedCache = new(allocator, SharedCacheSize);
|
_sharedCache = new(allocator, SharedCacheSize);
|
||||||
_localCache = new(allocator, LocalCacheSize);
|
_localCache = new(allocator, LocalCacheSize);
|
||||||
_pendingMap = new(_sharedCache.ReprotectAsRx, RegisterFunction);
|
_pendingMap = new(_sharedCache.ReprotectAsRx, RegisterFunction);
|
||||||
_lock = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe nint Map(nint framePointer, ReadOnlySpan<byte> code, ulong guestAddress, ulong guestSize)
|
public unsafe nint Map(nint framePointer, ReadOnlySpan<byte> code, ulong guestAddress, ulong guestSize)
|
||||||
|
@ -8,8 +8,6 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
public IEnumerable<ulong> GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size)
|
public IEnumerable<ulong> GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size)
|
||||||
{
|
{
|
||||||
List<ulong> functionPointers = new();
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
nint functionPointer = Marshal.ReadIntPtr(framePointer, nint.Size);
|
nint functionPointer = Marshal.ReadIntPtr(framePointer, nint.Size);
|
||||||
@ -20,11 +18,9 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
functionPointers.Add((ulong)functionPointer - 4);
|
yield return (ulong)functionPointer - 4;
|
||||||
framePointer = Marshal.ReadIntPtr(framePointer);
|
framePointer = Marshal.ReadIntPtr(framePointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return functionPointers;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user