mirror of
https://github.com/Ryubing/Ryujinx.git
synced 2025-07-13 04:03:12 +00:00
Compare commits
223 Commits
Canary-1.2
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7664f8cde9 | ||
![]() |
ddc00cf2d8 | ||
![]() |
05b4bd8c61 | ||
![]() |
20111a651c | ||
![]() |
659e580f4d | ||
![]() |
e6339ac950 | ||
![]() |
6b402847d2 | ||
![]() |
3b06f4cb78 | ||
![]() |
cdf60eecc0 | ||
![]() |
1219f329c1 | ||
![]() |
d5527f87cb | ||
![]() |
fc75dbc8b8 | ||
![]() |
04361b864a | ||
![]() |
7271e7050d | ||
![]() |
1ca4484148 | ||
![]() |
056f56bc70 | ||
![]() |
bda699f68e | ||
![]() |
0db85d0aa9 | ||
![]() |
44632e5d8b | ||
![]() |
551d2c1134 | ||
![]() |
25cc9b24b4 | ||
![]() |
638c616ab7 | ||
![]() |
109f0fc659 | ||
![]() |
dfcb8a7fc0 | ||
![]() |
d87d3235e9 | ||
![]() |
f3a9cecf72 | ||
![]() |
97a1bbdd74 | ||
![]() |
e379fad5da | ||
![]() |
c76f32a4ee | ||
![]() |
7bdf013ba6 | ||
![]() |
e07130ecc3 | ||
![]() |
dd02c8e25d | ||
![]() |
bed3835718 | ||
![]() |
2b06826922 | ||
![]() |
a23c6bf547 | ||
![]() |
27cdf876a2 | ||
![]() |
b0c0e8f7ad | ||
![]() |
2317c06364 | ||
![]() |
c12a59ecd6 | ||
![]() |
57c22a1f32 | ||
![]() |
f7976753fd | ||
![]() |
b45a65fbdc | ||
![]() |
c410474d83 | ||
![]() |
ffdc419417 | ||
![]() |
da3f4e1d3a | ||
![]() |
69d79322bb | ||
![]() |
c3af1dbf1a | ||
![]() |
10ac381525 | ||
![]() |
e104ee6be3 | ||
![]() |
e65d1ec6c9 | ||
![]() |
534f92506b | ||
![]() |
10d20c1ae3 | ||
![]() |
e294a79975 | ||
![]() |
ec06a86899 | ||
![]() |
2e4de17472 | ||
![]() |
9227cbe5a7 | ||
![]() |
332bcdfaf1 | ||
![]() |
1c8276197f | ||
![]() |
a3596ba858 | ||
![]() |
3ffcc72117 | ||
![]() |
fe1617ffea | ||
![]() |
eb6b0e9adc | ||
![]() |
9631bdfe16 | ||
![]() |
2a84656ffc | ||
![]() |
6c6580ddcc | ||
![]() |
c47448628c | ||
![]() |
d0ac83b493 | ||
![]() |
e0ddbe55c0 | ||
![]() |
4a4078865f | ||
![]() |
3f59bade94 | ||
![]() |
c2ed0fd5fd | ||
![]() |
de16d8fa3e | ||
![]() |
9b1fb3a27b | ||
![]() |
b9150a0092 | ||
![]() |
c1002d4826 | ||
![]() |
b1de7696ee | ||
![]() |
f91cd05260 | ||
![]() |
920933bc9f | ||
![]() |
52b0b45d34 | ||
![]() |
12f0dbcc70 | ||
![]() |
719be560ec | ||
![]() |
18238736be | ||
![]() |
5d9e4ad7a4 | ||
![]() |
adba775f0c | ||
![]() |
2ffaeb2803 | ||
![]() |
b16b844760 | ||
![]() |
bc07bc482d | ||
![]() |
61975ca44d | ||
![]() |
66054dd225 | ||
![]() |
b1f61e5143 | ||
![]() |
0d7d0e8092 | ||
![]() |
aa2178dbe5 | ||
![]() |
f92d09711b | ||
![]() |
45ee8cd0e8 | ||
![]() |
395bbd144a | ||
![]() |
744d813b87 | ||
![]() |
7d59ada798 | ||
![]() |
a4b5304935 | ||
![]() |
0965ee905d | ||
![]() |
855161b23b | ||
![]() |
6b55d158b7 | ||
![]() |
91f73a4891 | ||
![]() |
883d4d863a | ||
![]() |
ca5de909a1 | ||
![]() |
5172567b08 | ||
![]() |
6fe4cee7c0 | ||
![]() |
8623452abc | ||
![]() |
17e8ae1d9a | ||
![]() |
7591b07fce | ||
![]() |
89b4389ed2 | ||
![]() |
d9ee729199 | ||
![]() |
ba0cd13cff | ||
![]() |
501b199e24 | ||
![]() |
8aecccadb8 | ||
![]() |
e23d610f49 | ||
![]() |
f6822f7358 | ||
![]() |
d3f84a1305 | ||
![]() |
06d34a5992 | ||
![]() |
e8e1dc6619 | ||
![]() |
c5603d4c36 | ||
![]() |
05b56730d6 | ||
![]() |
43f7b000ca | ||
![]() |
ad89cf39b6 | ||
![]() |
96c33a0b92 | ||
![]() |
e0db55df46 | ||
![]() |
30fef8e96e | ||
![]() |
9cb5f5689b | ||
![]() |
a205ec374b | ||
![]() |
aab9b58542 | ||
![]() |
daa648dc40 | ||
![]() |
1024aa8757 | ||
![]() |
13388e972a | ||
![]() |
1eb78872d8 | ||
![]() |
fe9fe2a10f | ||
![]() |
6ab899f621 | ||
![]() |
faacec9801 | ||
![]() |
55fdb3f6b2 | ||
![]() |
1129ab0e8c | ||
![]() |
b6b391b2cf | ||
![]() |
f3cf03495d | ||
![]() |
7bce8206d5 | ||
![]() |
efa0cc7554 | ||
![]() |
1c0813d09d | ||
![]() |
8bec09d7ff | ||
![]() |
e4b4e94b56 | ||
![]() |
764c9e9d4e | ||
![]() |
05e991db87 | ||
![]() |
2cd876b1cb | ||
![]() |
93a298523f | ||
![]() |
253cbb2810 | ||
![]() |
9c226dcc7a | ||
![]() |
30a534edcd | ||
![]() |
1d88771d1b | ||
![]() |
4e8157688e | ||
![]() |
5085af0050 | ||
![]() |
2c8edaf89e | ||
![]() |
aa8ba8b503 | ||
![]() |
a4211fec33 | ||
![]() |
54b233dd78 | ||
![]() |
d1da937fce | ||
![]() |
4a8f98126f | ||
![]() |
e55629a908 | ||
![]() |
c638a7daf8 | ||
![]() |
5e5e180fea | ||
![]() |
131fe71205 | ||
![]() |
6af388c623 | ||
![]() |
45cec4e7cf | ||
![]() |
479b38f035 | ||
![]() |
3ecc7819cc | ||
![]() |
4b1d94ccd8 | ||
![]() |
4ae9f1c0d2 | ||
![]() |
717851985e | ||
![]() |
bd08a111a8 | ||
![]() |
1972a47f39 | ||
![]() |
222ceb818b | ||
![]() |
b0fcc5bee1 | ||
![]() |
820e8f7375 | ||
![]() |
e8a7d5b0b7 | ||
![]() |
fafb99c702 | ||
![]() |
df9e6e4812 | ||
![]() |
566f3d079a | ||
![]() |
d7707d4176 | ||
![]() |
7a9b62884a | ||
![]() |
de9faf183a | ||
![]() |
0bf7c5dfa2 | ||
![]() |
11bc32d98e | ||
![]() |
063430ea16 | ||
![]() |
65f08caaa3 | ||
![]() |
f225b18c05 | ||
![]() |
d8549f687b | ||
![]() |
5ab50680b4 | ||
![]() |
a0edc5c2b0 | ||
![]() |
158ea7b4d6 | ||
![]() |
8bc3de8303 | ||
![]() |
c812106611 | ||
![]() |
11e4d8f970 | ||
![]() |
774edb7b29 | ||
![]() |
55536f5d78 | ||
![]() |
b2eecd28ce | ||
![]() |
fe43c32e60 | ||
![]() |
8117e160c2 | ||
![]() |
bf713a80d6 | ||
![]() |
b38b5a1e70 | ||
![]() |
2d7700949c | ||
![]() |
ea2287af03 | ||
![]() |
37af8c70aa | ||
![]() |
50cee3fd19 | ||
![]() |
a46aacf2e2 | ||
![]() |
ad9d6588e8 | ||
![]() |
38ef65aae0 | ||
![]() |
9f94aa1c79 | ||
![]() |
2c9a26c11c | ||
![]() |
a4a15a4c80 | ||
![]() |
cc3b95eee1 | ||
![]() |
2ab806f759 | ||
![]() |
6d75410bd2 | ||
![]() |
196b2eaf66 | ||
![]() |
82fe519766 | ||
![]() |
ff05e03cc8 | ||
![]() |
e18c6d90c4 | ||
![]() |
9075a3960b | ||
![]() |
3cf54987d2 | ||
![]() |
9c12f52805 |
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -19,6 +19,7 @@ jobs:
|
|||||||
configuration: [Debug, Release]
|
configuration: [Debug, Release]
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||||
|
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||||
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
|
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
|
||||||
|
6
.github/workflows/canary.yml
vendored
6
.github/workflows/canary.yml
vendored
@ -29,7 +29,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
tag:
|
tag:
|
||||||
name: Create tag
|
name: Create tag
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
@ -62,6 +62,7 @@ jobs:
|
|||||||
| Platform | Artifact |
|
| 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) |
|
| 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) |
|
||||||
|
| Windows ARM 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_arm64.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 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) |
|
| 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) |
|
| 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) |
|
||||||
@ -79,6 +80,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||||
|
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||||
steps:
|
steps:
|
||||||
@ -202,7 +204,7 @@ jobs:
|
|||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
19
.github/workflows/release.yml
vendored
19
.github/workflows/release.yml
vendored
@ -12,13 +12,14 @@ env:
|
|||||||
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: "Ryubing"
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Stable-Releases"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tag:
|
tag:
|
||||||
name: Create tag
|
name: Create tag
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
@ -33,7 +34,7 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
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_SOURCE_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
|
||||||
})
|
})
|
||||||
@ -52,7 +53,7 @@ jobs:
|
|||||||
| 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) |
|
| 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) |
|
| 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 }}
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_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 }}
|
||||||
@ -65,6 +66,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||||
|
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||||
steps:
|
steps:
|
||||||
@ -92,7 +94,7 @@ jobs:
|
|||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -116,6 +118,7 @@ jobs:
|
|||||||
if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish
|
pushd publish
|
||||||
|
rm libarmeilleure-jitsupport.dylib
|
||||||
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
|
||||||
@ -173,7 +176,7 @@ jobs:
|
|||||||
| 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) |
|
| 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) |
|
| 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 }}
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_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
|
||||||
@ -183,7 +186,7 @@ jobs:
|
|||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -222,7 +225,7 @@ jobs:
|
|||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
@ -39,14 +39,13 @@
|
|||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
||||||
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
|
||||||
<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.7.1" />
|
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="Sep" Version="0.6.0" />
|
<PackageVersion Include="Sep" Version="0.6.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.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
@ -54,8 +53,8 @@
|
|||||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.0" />
|
<PackageVersion Include="System.Management" Version="9.0.2" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
10
README.md
10
README.md
@ -1,14 +1,14 @@
|
|||||||
<table align="center">
|
<table align="center">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" width="25%">
|
<td align="center" width="25%">
|
||||||
<img src="https://raw.githubusercontent.com/GreemDev/ryuassets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
|
<img src="https://raw.githubusercontent.com/Ryubing/Assets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
|
||||||
</td>
|
</td>
|
||||||
<td align="center" width="75%">
|
<td align="center" width="75%">
|
||||||
|
|
||||||
# Ryujinx
|
# Ryujinx
|
||||||
|
|
||||||
[](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml)
|
[](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml)
|
||||||
[](https://github.com/Ryubing/Ryujinx/releases/latest)
|
[](https://github.com/Ryubing/Ryujinx/releases/latest)
|
||||||
<br>
|
<br>
|
||||||
[](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml)
|
[](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml)
|
||||||
[](https://github.com/Ryubing/Canary-Releases/releases/latest)
|
[](https://github.com/Ryubing/Canary-Releases/releases/latest)
|
||||||
@ -39,12 +39,12 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
Click below to join the Discord:
|
Click below to join the Discord:
|
||||||
<br>
|
<br>
|
||||||
<a href="https://discord.gg/dHPrkBkkyA">
|
<a href="https://discord.gg/PEuzjrFXUA">
|
||||||
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
|
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -97,7 +97,7 @@ If you are planning to contribute or just want to learn more about this project
|
|||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers.
|
We currently have support for keyboard, mouse, touch input, Joy-Con input support, and nearly all controllers.
|
||||||
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
||||||
In all scenarios, you can set up everything inside the input configuration menu.
|
In all scenarios, you can set up everything inside the input configuration menu.
|
||||||
|
|
||||||
|
24
Ryujinx.sln
24
Ryujinx.sln
@ -77,15 +77,6 @@ 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
|
||||||
@ -95,6 +86,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
.github\workflows\release.yml = .github\workflows\release.yml
|
.github\workflows\release.yml = .github\workflows\release.yml
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -217,6 +210,10 @@ 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
|
||||||
@ -249,16 +246,9 @@ 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}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|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
|
||||||
|
@ -332,6 +332,7 @@
|
|||||||
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
|
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
|
||||||
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
|
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
|
||||||
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
|
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
|
||||||
|
01008d8006a6a000,"Arena of Valor",crash,boots,2025-02-03 22:19:34
|
||||||
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
|
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
|
||||||
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
|
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
|
||||||
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
|
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
|
||||||
@ -426,6 +427,7 @@
|
|||||||
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
|
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
|
||||||
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
|
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
|
||||||
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
|
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
|
||||||
|
0100fd1014726000,"Baldur's Gate: Dark Alliance",ldn-untested,ingame,2025-02-03 22:21:00
|
||||||
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
|
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
|
||||||
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
|
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
|
||||||
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
|
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
|
||||||
@ -528,6 +530,7 @@
|
|||||||
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
|
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
|
||||||
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
|
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
|
||||||
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
|
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
|
||||||
|
01003d700dd8a000,"Blades",,boots,2025-02-03 22:22:00
|
||||||
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
|
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
|
||||||
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
|
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
|
||||||
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
|
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
|
||||||
@ -628,6 +631,7 @@
|
|||||||
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
|
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
|
||||||
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
|
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
|
||||||
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17
|
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17
|
||||||
|
01008c2019598000,"Bluey: The Videogame",,playable,2025-02-11 04:38:00
|
||||||
01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40
|
01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40
|
||||||
010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15
|
010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15
|
||||||
01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
|
01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
|
||||||
@ -955,7 +959,7 @@
|
|||||||
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
|
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
|
||||||
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
|
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
|
||||||
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
|
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
|
||||||
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,menus,2023-08-13 17:20:03
|
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,ingame,2025-02-03 22:12:30
|
||||||
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
|
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
|
||||||
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16
|
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16
|
||||||
010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
|
010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
|
||||||
@ -966,6 +970,7 @@
|
|||||||
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
|
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
|
||||||
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
|
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
|
||||||
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
|
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
|
||||||
|
01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12
|
||||||
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
|
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
|
||||||
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
||||||
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
||||||
@ -1155,7 +1160,7 @@
|
|||||||
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
|
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
|
||||||
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
|
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
|
||||||
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
|
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
|
||||||
01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30
|
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30
|
||||||
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
|
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
|
||||||
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
|
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
|
||||||
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
|
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
|
||||||
@ -1246,7 +1251,7 @@
|
|||||||
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
|
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
|
||||||
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
|
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
|
||||||
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
|
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
|
||||||
0100E1F013674000,"FUSER™",nvdec;UE4,playable,2022-10-17 20:58:32
|
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00
|
||||||
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
|
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
|
||||||
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
|
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
|
||||||
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
|
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
|
||||||
@ -1378,6 +1383,9 @@
|
|||||||
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34
|
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34
|
||||||
01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
|
01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
|
||||||
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51
|
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51
|
||||||
|
0100c3c012718000,"Grand Theft Auto: III – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||||
|
0100182014022000,"Grand Theft Auto: Vice City – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||||
|
010065a014024000,"Grand Theft Auto: San Andreas – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||||
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
|
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
|
||||||
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
|
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
|
||||||
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
|
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
|
||||||
@ -1432,7 +1440,7 @@
|
|||||||
010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50
|
010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50
|
||||||
01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19
|
01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19
|
||||||
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
|
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
|
||||||
0100F7E00C70E000,"Hogwarts Legacy",slow,ingame,2024-09-03 19:53:58
|
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
|
||||||
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
|
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
|
||||||
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
|
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
|
||||||
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
|
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
|
||||||
@ -1654,7 +1662,7 @@
|
|||||||
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
|
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
|
||||||
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
|
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
|
||||||
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
|
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
|
||||||
01003A30012C0000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
|
010085500130a000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
|
||||||
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37
|
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37
|
||||||
010052A00B5D2000,"LEGO® Harry Potter™ Collection",crash,ingame,2024-01-31 10:28:07
|
010052A00B5D2000,"LEGO® Harry Potter™ Collection",crash,ingame,2024-01-31 10:28:07
|
||||||
010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
|
010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
|
||||||
@ -1796,6 +1804,7 @@
|
|||||||
010005A00B312000,"Megaton Rainfall",gpu;opengl,boots,2022-08-04 18:29:43
|
010005A00B312000,"Megaton Rainfall",gpu;opengl,boots,2022-08-04 18:29:43
|
||||||
0100EA100DF92000,"Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou",32-bit;nvdec,playable,2022-12-05 13:19:12
|
0100EA100DF92000,"Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou",32-bit;nvdec,playable,2022-12-05 13:19:12
|
||||||
0100B360068B2000,"Mekorama",gpu,boots,2021-06-17 16:37:21
|
0100B360068B2000,"Mekorama",gpu,boots,2021-06-17 16:37:21
|
||||||
|
010012301932A000,"Melatonin",,playable,2025-02-16 04:08:17
|
||||||
01000FA010340000,"Melbits World",nvdec;online,menus,2021-11-26 13:51:22
|
01000FA010340000,"Melbits World",nvdec;online,menus,2021-11-26 13:51:22
|
||||||
0100F68019636000,"Melon Journey",,playable,2023-04-23 21:20:01
|
0100F68019636000,"Melon Journey",,playable,2023-04-23 21:20:01
|
||||||
010079C012896000,"Memories Off -Innocent Fille- for Dearest",,playable,2020-08-04 07:31:22
|
010079C012896000,"Memories Off -Innocent Fille- for Dearest",,playable,2020-08-04 07:31:22
|
||||||
@ -1913,6 +1922,7 @@
|
|||||||
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
|
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
|
||||||
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
|
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
|
||||||
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
|
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
|
||||||
|
01008e2013fb4000,"Multi Quiz",ldn-untested,ingame,2025-02-03 22:26:00
|
||||||
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
|
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
|
||||||
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
|
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
|
||||||
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
|
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
|
||||||
@ -2028,6 +2038,7 @@
|
|||||||
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
|
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
|
||||||
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
|
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
|
||||||
0100C9A00ECE6000,"Nintendo 64™ – Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
|
0100C9A00ECE6000,"Nintendo 64™ – Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
|
||||||
|
0100e0601c632000,"Nintendo 64™ – Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
|
||||||
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
|
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
|
||||||
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
|
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
|
||||||
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
|
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
|
||||||
@ -2058,7 +2069,7 @@
|
|||||||
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
|
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
|
||||||
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
|
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
|
||||||
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
|
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
|
||||||
0000000000000000,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
|
,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
|
||||||
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04
|
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04
|
||||||
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
|
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
|
||||||
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
|
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
|
||||||
@ -2466,7 +2477,7 @@
|
|||||||
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
|
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
|
||||||
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
|
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
|
||||||
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
|
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
|
||||||
0000000000000000,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
|
,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
|
||||||
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53
|
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53
|
||||||
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
|
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
|
||||||
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
|
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
|
||||||
@ -2475,6 +2486,7 @@
|
|||||||
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
|
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
|
||||||
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
|
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
|
||||||
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
|
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
|
||||||
|
010071E0145F8000,"Rustler",,playable,2025-02-10 20:17:12
|
||||||
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
|
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
|
||||||
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
|
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
|
||||||
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
|
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
|
||||||
@ -2483,7 +2495,7 @@
|
|||||||
0100A5200C2E0000,"Safety First!",,playable,2021-01-06 09:05:23
|
0100A5200C2E0000,"Safety First!",,playable,2021-01-06 09:05:23
|
||||||
0100A51013530000,"SaGa Frontier Remastered",nvdec,playable,2022-11-03 13:54:56
|
0100A51013530000,"SaGa Frontier Remastered",nvdec,playable,2022-11-03 13:54:56
|
||||||
010003A00D0B4000,"SaGa SCARLET GRACE: AMBITIONS™",,playable,2022-10-06 13:20:31
|
010003A00D0B4000,"SaGa SCARLET GRACE: AMBITIONS™",,playable,2022-10-06 13:20:31
|
||||||
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN,playable,2023-12-04 18:33:37
|
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN;deadlock,ingame,2025-02-02 16:57:53
|
||||||
0100DE600BEEE000,"SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE",slow;LAN,playable,2023-08-24 02:40:58
|
0100DE600BEEE000,"SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE",slow;LAN,playable,2023-08-24 02:40:58
|
||||||
01007F000EB36000,"Sakai and...",nvdec,playable,2022-12-15 13:53:19
|
01007F000EB36000,"Sakai and...",nvdec,playable,2022-12-15 13:53:19
|
||||||
0100B1400E8FE000,"Sakuna: Of Rice and Ruin",,playable,2023-07-24 13:47:13
|
0100B1400E8FE000,"Sakuna: Of Rice and Ruin",,playable,2023-07-24 13:47:13
|
||||||
@ -2532,7 +2544,7 @@
|
|||||||
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
|
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
|
||||||
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
|
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
|
||||||
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
|
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
|
||||||
0100B3C014BDA000,"SEGA Genesis™ – Nintendo Switch Online",crash;regression,nothing,2022-04-11 07:27:21
|
0100B3C014BDA000,"SEGA Genesis™ – Nintendo Switch Online",crash;regression,ingame,2025-02-03 22:13:30
|
||||||
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
|
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
|
||||||
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
|
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
|
||||||
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
|
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
|
||||||
@ -2668,10 +2680,10 @@
|
|||||||
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
|
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
|
||||||
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
|
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
|
||||||
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
|
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
|
||||||
0000000000000000,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
|
,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
|
||||||
0000000000000000,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
|
,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
|
||||||
0000000000000000,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
|
,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
|
||||||
0000000000000000,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
|
,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
|
||||||
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
|
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
|
||||||
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
|
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
|
||||||
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
|
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
|
||||||
@ -2688,7 +2700,7 @@
|
|||||||
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
|
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
|
||||||
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
|
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
|
||||||
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
|
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
|
||||||
0000000000000000,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
|
,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
|
||||||
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
|
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
|
||||||
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
|
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
|
||||||
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
|
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
|
||||||
@ -2721,7 +2733,7 @@
|
|||||||
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11
|
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11
|
||||||
0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12
|
0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12
|
||||||
010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34
|
010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34
|
||||||
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53
|
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00
|
||||||
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
|
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
|
||||||
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
|
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
|
||||||
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42
|
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42
|
||||||
@ -2832,8 +2844,9 @@
|
|||||||
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
||||||
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
|
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
|
||||||
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
||||||
|
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
|
||||||
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
||||||
0000000000000000,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
|
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
|
||||||
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
|
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
|
||||||
010028600EBDA000,"Super Mario™ 3D World + Bowser’s Fury",ldn-works,playable,2024-07-31 10:45:37
|
010028600EBDA000,"Super Mario™ 3D World + Bowser’s Fury",ldn-works,playable,2024-07-31 10:45:37
|
||||||
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
|
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
|
||||||
@ -2964,6 +2977,7 @@
|
|||||||
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
|
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
|
||||||
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
|
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
|
||||||
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
|
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
|
||||||
|
0100b620139d8000,"The Game of Life 2",ldn-untested,ingame,2025-02-03 22:30:00
|
||||||
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
|
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
|
||||||
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
|
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
|
||||||
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
|
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
|
||||||
@ -2981,6 +2995,8 @@
|
|||||||
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
|
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
|
||||||
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
|
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
|
||||||
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
|
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
|
||||||
|
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
|
||||||
|
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
|
||||||
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
|
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
|
||||||
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
|
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
|
||||||
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
|
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
|
||||||
@ -3159,6 +3175,7 @@
|
|||||||
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
|
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
|
||||||
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
|
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
|
||||||
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
|
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
|
||||||
|
010020700a5e0000,"TRIVIAL PURSUIT Live!",ldn-untested,ingame,2025-02-03 22:35:00
|
||||||
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
|
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
|
||||||
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
|
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
|
||||||
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
|
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
|
||||||
@ -3208,6 +3225,7 @@
|
|||||||
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
|
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
|
||||||
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
|
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
|
||||||
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
|
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
|
||||||
|
0100b6e012ebe000,"UNO",ldn-untested,ingame,2025-02-03 22:40:00
|
||||||
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
|
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
|
||||||
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
|
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
|
||||||
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
|
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
|
||||||
@ -3372,6 +3390,7 @@
|
|||||||
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
|
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
|
||||||
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
|
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
|
||||||
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
|
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
|
||||||
|
0100534009ff2000,"Yonder: The Cloud Catcher Chronicles",,playable,2025-02-03 22:19:13
|
||||||
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
|
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
|
||||||
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
|
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
|
||||||
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21
|
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21
|
||||||
|
|
@ -1,7 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Translation.Cache;
|
using ARMeilleure.Translation.Cache;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen
|
namespace ARMeilleure.CodeGen
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ namespace ARMeilleure.Memory
|
|||||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||||
|
|
||||||
public IJitMemoryBlock Block { get; }
|
public IJitMemoryBlock Block { get; }
|
||||||
|
public IJitMemoryAllocator Allocator { get; }
|
||||||
|
|
||||||
public nint Pointer => Block.Pointer;
|
public nint Pointer => Block.Pointer;
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ namespace ARMeilleure.Memory
|
|||||||
granularity = DefaultGranularity;
|
granularity = DefaultGranularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Allocator = allocator;
|
||||||
Block = allocator.Reserve(maxSize);
|
Block = allocator.Reserve(maxSize);
|
||||||
_maxSize = maxSize;
|
_maxSize = maxSize;
|
||||||
_sizeGranularity = granularity;
|
_sizeGranularity = granularity;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ using ARMeilleure.Instructions;
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
@ -2,6 +2,8 @@ using ARMeilleure.CodeGen;
|
|||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Native;
|
using ARMeilleure.Native;
|
||||||
|
using Humanizer;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -18,51 +20,68 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = [];
|
private static readonly List<CacheEntry> _cacheEntries = [];
|
||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = [];
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if (_initialized)
|
||||||
{
|
{
|
||||||
return;
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
JitUnwindWindows.RemoveFunctionTableHandler(
|
||||||
|
_jitRegions[0].Pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _jitRegions.Count; i++)
|
||||||
|
{
|
||||||
|
_jitRegions[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_jitRegions.Clear();
|
||||||
|
_cacheAllocators.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
|
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
|
||||||
|
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
|
||||||
|
_cacheAllocators.Add(firstCacheAllocator);
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
JitUnwindWindows.InstallFunctionTableHandler(
|
||||||
|
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initialized = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +94,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@ -90,9 +109,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@ -116,52 +135,77 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (ReservedRegion region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||||
|
|
||||||
if (allocOffset < 0)
|
if (allocOffset >= 0)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
return allocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
return allocOffset;
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocators.Add(new CacheMemoryAllocator(CacheSize));
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
{
|
{
|
||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
@ -185,18 +229,21 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
foreach (ReservedRegion _ in _jitRegions)
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
{
|
||||||
index = ~index - 1;
|
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
entry = _cacheEntries[index];
|
index = ~index - 1;
|
||||||
entryIndex = index;
|
}
|
||||||
return true;
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
entry = _cacheEntries[index];
|
||||||
|
entryIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
|
@ -52,6 +52,11 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
nint context,
|
nint context,
|
||||||
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
||||||
|
|
||||||
|
[LibraryImport("kernel32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static unsafe partial bool RtlDeleteFunctionTable(
|
||||||
|
ulong tableIdentifier);
|
||||||
|
|
||||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||||
|
|
||||||
private static int _sizeOfRuntimeFunction;
|
private static int _sizeOfRuntimeFunction;
|
||||||
@ -91,6 +96,23 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
||||||
|
{
|
||||||
|
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
result = RtlDeleteFunctionTable(codeCachePtr | 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failure removing function table callback.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
||||||
{
|
{
|
||||||
int offset = (int)((long)controlPc - context.ToInt64());
|
int offset = (int)((long)controlPc - context.ToInt64());
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
class DelegateInfo
|
class DelegateInfo
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
delegate void DispatcherFunction(nint nativeContext, ulong startAddress);
|
delegate void DispatcherFunction(nint nativeContext, ulong startAddress);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
delegate ulong GuestFunction(nint nativeContextPtr);
|
delegate ulong GuestFunction(nint nativeContextPtr);
|
||||||
|
@ -144,17 +144,15 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public List<ulong> GetBlacklistedFunctions()
|
public List<ulong> GetBlacklistedFunctions()
|
||||||
{
|
{
|
||||||
List<ulong> funcs = new List<ulong>();
|
List<ulong> funcs = [];
|
||||||
|
|
||||||
foreach (var profiledFunc in ProfiledFuncs)
|
foreach ((ulong ptr, FuncProfile funcProfile) in ProfiledFuncs)
|
||||||
{
|
{
|
||||||
if (profiledFunc.Value.Blacklist)
|
if (!funcProfile.Blacklist)
|
||||||
{
|
continue;
|
||||||
if (!funcs.Contains(profiledFunc.Key))
|
|
||||||
{
|
if (!funcs.Contains(ptr))
|
||||||
funcs.Add(profiledFunc.Key);
|
funcs.Add(ptr);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return funcs;
|
return funcs;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,6 @@ using ARMeilleure.Diagnostics;
|
|||||||
using ARMeilleure.Instructions;
|
using ARMeilleure.Instructions;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Signal;
|
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.Cache;
|
using ARMeilleure.Translation.Cache;
|
||||||
using ARMeilleure.Translation.PTC;
|
using ARMeilleure.Translation.PTC;
|
||||||
|
@ -4,7 +4,6 @@ using ARMeilleure.IntermediateRepresentation;
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.Cache;
|
using ARMeilleure.Translation.Cache;
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64</RuntimeIdentifiers>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@ -11,15 +11,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dll</TargetPath>
|
<TargetPath>libsoundio.dll</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dylib</TargetPath>
|
<TargetPath>libsoundio.dylib</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.so</TargetPath>
|
<TargetPath>libsoundio.so</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
|
@ -4,7 +4,6 @@ using Ryujinx.Audio.Common;
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Backends.Common
|
namespace Ryujinx.Audio.Backends.Common
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using CpuAddress = System.UInt64;
|
using CpuAddress = System.UInt64;
|
||||||
using DspAddress = System.UInt64;
|
using DspAddress = System.UInt64;
|
||||||
|
@ -9,7 +9,8 @@ namespace Ryujinx.Common.Configuration
|
|||||||
public enum DirtyHack : byte
|
public enum DirtyHack : byte
|
||||||
{
|
{
|
||||||
Xc2MenuSoftlockFix = 1,
|
Xc2MenuSoftlockFix = 1,
|
||||||
ShaderTranslationDelay = 2
|
// ShaderTranslationDelay = 2
|
||||||
|
NifmServiceDisableIsAnyInternetRequestAccepted = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct EnabledDirtyHack(DirtyHack hack, int value)
|
public readonly struct EnabledDirtyHack(DirtyHack hack, int value)
|
||||||
|
@ -6,9 +6,7 @@ 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,5 +1,4 @@
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
118
src/Ryujinx.Common/Helpers/Patterns.cs
Normal file
118
src/Ryujinx.Common/Helpers/Patterns.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Helper
|
||||||
|
{
|
||||||
|
public static partial class Patterns
|
||||||
|
{
|
||||||
|
#region Accessors
|
||||||
|
|
||||||
|
public static readonly Regex Numeric = NumericRegex();
|
||||||
|
|
||||||
|
public static readonly Regex AmdGcn = AmdGcnRegex();
|
||||||
|
public static readonly Regex NvidiaConsumerClass = NvidiaConsumerClassRegex();
|
||||||
|
|
||||||
|
public static readonly Regex DomainLp1Ns = DomainLp1NsRegex();
|
||||||
|
public static readonly Regex DomainLp1Lp1Npln = DomainLp1Lp1NplnRegex();
|
||||||
|
public static readonly Regex DomainLp1Znc = DomainLp1ZncRegex();
|
||||||
|
public static readonly Regex DomainSbApi = DomainSbApiRegex();
|
||||||
|
public static readonly Regex DomainSbAccounts = DomainSbAccountsRegex();
|
||||||
|
public static readonly Regex DomainAccounts = DomainAccountsRegex();
|
||||||
|
|
||||||
|
public static readonly Regex Module = ModuleRegex();
|
||||||
|
public static readonly Regex FsSdk = FsSdkRegex();
|
||||||
|
public static readonly Regex SdkMw = SdkMwRegex();
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public static readonly Regex CJK = CJKRegex();
|
||||||
|
|
||||||
|
public static readonly Regex LdnPassphrase = LdnPassphraseRegex();
|
||||||
|
|
||||||
|
public static readonly Regex CleanText = CleanTextRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Generated pattern stubs
|
||||||
|
|
||||||
|
#region Numeric validation
|
||||||
|
|
||||||
|
[GeneratedRegex("[0-9]|.")]
|
||||||
|
internal static partial Regex NumericRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GPU names
|
||||||
|
|
||||||
|
[GeneratedRegex(
|
||||||
|
"Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
|
||||||
|
internal static partial Regex AmdGcnRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
|
||||||
|
internal static partial Regex NvidiaConsumerClassRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DNS blocking
|
||||||
|
|
||||||
|
public static readonly Regex[] BlockedHosts =
|
||||||
|
[
|
||||||
|
DomainLp1Ns,
|
||||||
|
DomainLp1Lp1Npln,
|
||||||
|
DomainLp1Znc,
|
||||||
|
DomainSbApi,
|
||||||
|
DomainSbAccounts,
|
||||||
|
DomainAccounts
|
||||||
|
];
|
||||||
|
|
||||||
|
const RegexOptions DnsRegexOpts =
|
||||||
|
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", DnsRegexOpts)]
|
||||||
|
internal static partial Regex DomainLp1NsRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", DnsRegexOpts)]
|
||||||
|
internal static partial Regex DomainLp1Lp1NplnRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", DnsRegexOpts)]
|
||||||
|
internal static partial Regex DomainLp1ZncRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", DnsRegexOpts)]
|
||||||
|
internal static partial Regex DomainSbApiRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", DnsRegexOpts)]
|
||||||
|
internal static partial Regex DomainSbAccountsRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^accounts\.nintendo\.com$", DnsRegexOpts)]
|
||||||
|
internal static partial Regex DomainAccountsRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Executable information
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||||
|
internal static partial Regex ModuleRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
|
||||||
|
internal static partial Regex FsSdkRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"SDK MW[ -~]*")]
|
||||||
|
internal static partial Regex SdkMwRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region CJK
|
||||||
|
|
||||||
|
[GeneratedRegex(
|
||||||
|
"\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
|
||||||
|
private static partial Regex CJKRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
|
||||||
|
private static partial Regex LdnPassphraseRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
|
||||||
|
private static partial Regex CleanTextRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -5,19 +5,42 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace Ryujinx.Common.Helper
|
namespace Ryujinx.Common.Helper
|
||||||
{
|
{
|
||||||
|
public enum OperatingSystemType
|
||||||
|
{
|
||||||
|
MacOS,
|
||||||
|
Linux,
|
||||||
|
Windows
|
||||||
|
}
|
||||||
|
|
||||||
public static class RunningPlatform
|
public static class RunningPlatform
|
||||||
{
|
{
|
||||||
|
public static readonly OperatingSystemType CurrentOS
|
||||||
|
= IsMacOS
|
||||||
|
? OperatingSystemType.MacOS
|
||||||
|
: IsWindows
|
||||||
|
? OperatingSystemType.Windows
|
||||||
|
: IsLinux
|
||||||
|
? OperatingSystemType.Linux
|
||||||
|
: throw new PlatformNotSupportedException();
|
||||||
|
|
||||||
|
public static Architecture Architecture => RuntimeInformation.OSArchitecture;
|
||||||
|
public static Architecture CurrentProcessArchitecture => RuntimeInformation.ProcessArchitecture;
|
||||||
|
|
||||||
public static bool IsMacOS => OperatingSystem.IsMacOS();
|
public static bool IsMacOS => OperatingSystem.IsMacOS();
|
||||||
public static bool IsWindows => OperatingSystem.IsWindows();
|
public static bool IsWindows => OperatingSystem.IsWindows();
|
||||||
public static bool IsLinux => OperatingSystem.IsLinux();
|
public static bool IsLinux => OperatingSystem.IsLinux();
|
||||||
|
|
||||||
|
public static bool IsArm => Architecture is Architecture.Arm64;
|
||||||
|
|
||||||
|
public static bool IsX64 => Architecture is Architecture.X64;
|
||||||
|
|
||||||
public static bool IsIntelMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.X64;
|
public static bool IsIntelMac => IsMacOS && IsX64;
|
||||||
public static bool IsArmMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.Arm64;
|
public static bool IsArmMac => IsMacOS && IsArm;
|
||||||
|
|
||||||
public static bool IsX64Windows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.X64);
|
public static bool IsX64Windows => IsWindows && IsX64;
|
||||||
public static bool IsArmWindows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
|
public static bool IsArmWindows => IsWindows && IsArm;
|
||||||
|
|
||||||
public static bool IsX64Linux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.X64);
|
public static bool IsX64Linux => IsLinux && IsX64;
|
||||||
public static bool IsArmLinux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
|
public static bool IsArmLinux => IsLinux && IsArmMac;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@ namespace Ryujinx.Common
|
|||||||
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
||||||
IsCanaryBuild
|
IsCanaryBuild
|
||||||
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
||||||
: $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}";
|
: GetChangelogForVersion(newVersion);
|
||||||
|
|
||||||
public static string GetChangelogForVersion(Version version) =>
|
public static string GetChangelogForVersion(Version version) =>
|
||||||
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}";
|
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/{version}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
src/Ryujinx.Common/SharedConstants.cs
Normal file
9
src/Ryujinx.Common/SharedConstants.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public static class SharedConstants
|
||||||
|
{
|
||||||
|
public const string DefaultLanPlayHost = "ryuldn.vudjun.com";
|
||||||
|
public const short LanPlayPort = 30456;
|
||||||
|
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,5 @@
|
|||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Common.Helper;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
{
|
{
|
||||||
@ -11,54 +7,6 @@ namespace Ryujinx.Common
|
|||||||
{
|
{
|
||||||
public static ReactiveObject<Optional<string>> CurrentApplication { get; } = new();
|
public static ReactiveObject<Optional<string>> CurrentApplication { get; } = new();
|
||||||
|
|
||||||
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
|
|
||||||
{
|
|
||||||
switch (currentBackend)
|
|
||||||
{
|
|
||||||
case GraphicsBackend.Metal when !OperatingSystem.IsMacOS():
|
|
||||||
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS():
|
|
||||||
return GraphicsBackend.Vulkan;
|
|
||||||
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal:
|
|
||||||
return currentBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!RunningPlatform.IsArmMac)
|
|
||||||
return GraphicsBackend.Vulkan;
|
|
||||||
|
|
||||||
return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly string[] GreatMetalTitles =
|
|
||||||
[
|
|
||||||
"01009b500007c000", // ARMS
|
|
||||||
"0100a5c00d162000", // Cuphead
|
|
||||||
"010023800d64a000", // Deltarune
|
|
||||||
"01003a30012c0000", // LEGO City Undercover
|
|
||||||
"010048701995e000", // Luigi's Manion 2 HD
|
|
||||||
"010028600EBDA000", // Mario 3D World
|
|
||||||
"0100152000022000", // Mario Kart 8 Deluxe
|
|
||||||
"010075a016a3a000", // Persona 4 Arena Ultimax
|
|
||||||
"0100187003A36000", // Pokémon: Let's Go, Eevee!
|
|
||||||
"010003f003a34000", // Pokémon: Let's Go, Pikachu!
|
|
||||||
"01008C0016544000", // Sea of Stars
|
|
||||||
"01006A800016E000", // Smash Ultimate
|
|
||||||
"01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
|
|
||||||
|
|
||||||
// These ones have small issues, but those happen on Vulkan as well:
|
|
||||||
"01006f8002326000", // Animal Crossings: New Horizons
|
|
||||||
"01009bf0072d4000", // Captain Toad: Treasure Tracker
|
|
||||||
"01009510001ca000", // Fast RMX
|
|
||||||
"01005CA01580E000", // Persona 5 Royale
|
|
||||||
"010015100b514000", // Super Mario Bros. Wonder
|
|
||||||
"0100000000010000", // Super Mario Odyssey
|
|
||||||
|
|
||||||
// Further testing is appreciated, I did not test the entire game:
|
|
||||||
"01007300020fa000", // Astral Chain
|
|
||||||
"010076f0049a2000", // Bayonetta
|
|
||||||
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
|
||||||
"0100f4300bf2c000", // New Pokemon Snap
|
|
||||||
];
|
|
||||||
|
|
||||||
public static string GetDiscordGameAsset(string titleId)
|
public static string GetDiscordGameAsset(string titleId)
|
||||||
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
|
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
|
||||||
|
|
||||||
@ -164,15 +112,16 @@ namespace Ryujinx.Common
|
|||||||
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
||||||
|
|
||||||
//NSO Membership games
|
//NSO Membership games
|
||||||
"0100ccf019c8c000", // F-ZERO 99
|
|
||||||
"0100c62011050000", // GB - Nintendo Switch Online
|
"0100c62011050000", // GB - Nintendo Switch Online
|
||||||
"010012f017576000", // GBA - Nintendo Switch Online
|
"010012f017576000", // GBA - Nintendo Switch Online
|
||||||
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
||||||
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
||||||
"0100d870045b6000", // NES - Nintendo Switch Online
|
"0100d870045b6000", // NES - Nintendo Switch Online
|
||||||
|
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
|
||||||
|
"01008d300c50c000", // SNES - Nintendo Switch Online
|
||||||
|
"0100ccf019c8c000", // F-ZERO 99
|
||||||
"0100ad9012510000", // PAC-MAN 99
|
"0100ad9012510000", // PAC-MAN 99
|
||||||
"010040600c5ce000", // Tetris 99
|
"010040600c5ce000", // Tetris 99
|
||||||
"01008d300c50c000", // SNES - Nintendo Switch Online
|
|
||||||
"0100277011f1a000", // Super Mario Bros. 35
|
"0100277011f1a000", // Super Mario Bros. 35
|
||||||
|
|
||||||
//Misc Nintendo 1st party games
|
//Misc Nintendo 1st party games
|
||||||
@ -218,6 +167,7 @@ namespace Ryujinx.Common
|
|||||||
//Misc Games
|
//Misc Games
|
||||||
"010056e00853a000", // A Hat in Time
|
"010056e00853a000", // A Hat in Time
|
||||||
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
||||||
|
"01008c2019598000", // Bluey: The Video Game
|
||||||
"0100c6800b934000", // Brawlhalla
|
"0100c6800b934000", // Brawlhalla
|
||||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||||
"0100744001588000", // Cars 3: Driven to Win
|
"0100744001588000", // Cars 3: Driven to Win
|
||||||
@ -228,6 +178,7 @@ namespace Ryujinx.Common
|
|||||||
"01008c8012920000", // Dying Light Platinum Edition
|
"01008c8012920000", // Dying Light Platinum Edition
|
||||||
"01001cc01b2d4000", // Goat Simulator 3
|
"01001cc01b2d4000", // Goat Simulator 3
|
||||||
"01003620068ea000", // Hand of Fate 2
|
"01003620068ea000", // Hand of Fate 2
|
||||||
|
"0100f7e00c70e000", // Hogwarts Legacy
|
||||||
"010085500130a000", // Lego City: Undercover
|
"010085500130a000", // Lego City: Undercover
|
||||||
"010073c01af34000", // LEGO Horizon Adventures
|
"010073c01af34000", // LEGO Horizon Adventures
|
||||||
"0100d71004694000", // Minecraft
|
"0100d71004694000", // Minecraft
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ using ARMeilleure.Common;
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Cpu.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
using Ryujinx.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Jit
|
namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -8,7 +8,6 @@ using Ryujinx.Memory.Tracking;
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Jit
|
namespace Ryujinx.Cpu.Jit
|
||||||
|
@ -3,7 +3,6 @@ using ARMeilleure.Memory;
|
|||||||
using Ryujinx.Cpu.LightningJit.Arm32;
|
using Ryujinx.Cpu.LightningJit.Arm32;
|
||||||
using Ryujinx.Cpu.LightningJit.Arm64;
|
using Ryujinx.Cpu.LightningJit.Arm64;
|
||||||
using Ryujinx.Cpu.LightningJit.State;
|
using Ryujinx.Cpu.LightningJit.State;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit
|
namespace Ryujinx.Cpu.LightningJit
|
||||||
|
@ -2,7 +2,6 @@ using ARMeilleure.Common;
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
{
|
{
|
||||||
static class InstEmitVfpMove
|
static class InstEmitVfpMove
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Arm64
|
namespace Ryujinx.Cpu.LightningJit.Arm64
|
||||||
{
|
{
|
||||||
static class SysUtils
|
static class SysUtils
|
||||||
|
@ -3,7 +3,6 @@ using ARMeilleure.Memory;
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
using Ryujinx.Cpu.LightningJit.Graph;
|
using Ryujinx.Cpu.LightningJit.Graph;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using Humanizer;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -15,9 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
@ -26,6 +27,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = [];
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
@ -45,7 +48,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
@ -65,8 +70,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@ -80,18 +85,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||||
{
|
|
||||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(funcOffset, code.Length);
|
Add(funcOffset, code.Length);
|
||||||
@ -106,50 +104,80 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (ReservedRegion region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
if (allocOffset < 0)
|
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (allocOffset >= 0)
|
||||||
|
{
|
||||||
|
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
_activeRegionIndex = i;
|
||||||
|
return allocOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
|
int newRegionNumber = _activeRegionIndex;
|
||||||
|
|
||||||
return allocOffset;
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int SharedCacheSize = 2047 * 1024 * 1024;
|
private const int SharedCacheSize = 2047 * 1024 * 1024;
|
||||||
private const int LocalCacheSize = 128 * 1024 * 1024;
|
private const int LocalCacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
||||||
// and allow the guest to take the fast path.
|
// and allow the guest to take the fast path.
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit
|
namespace Ryujinx.Cpu.LightningJit
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit
|
namespace Ryujinx.Cpu.LightningJit
|
||||||
{
|
{
|
||||||
class TranslatedFunction
|
class TranslatedFunction
|
||||||
|
@ -5,7 +5,6 @@ using Ryujinx.Cpu.LightningJit.Cache;
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
using Ryujinx.Cpu.LightningJit.State;
|
using Ryujinx.Cpu.LightningJit.State;
|
||||||
using Ryujinx.Cpu.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
using Ryujinx.Memory;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Signal
|
namespace Ryujinx.Cpu.Signal
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using Ryujinx.Common.Logging;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Device
|
namespace Ryujinx.Graphics.Device
|
||||||
{
|
{
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.GAL
|
|
||||||
{
|
|
||||||
public readonly struct ComputeSize
|
|
||||||
{
|
|
||||||
public readonly static ComputeSize VtgAsCompute = new(32, 32, 1);
|
|
||||||
|
|
||||||
public readonly int X;
|
|
||||||
public readonly int Y;
|
|
||||||
public readonly int Z;
|
|
||||||
|
|
||||||
public ComputeSize(int x, int y, int z)
|
|
||||||
{
|
|
||||||
X = x;
|
|
||||||
Y = y;
|
|
||||||
Z = z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -339,84 +339,6 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get bytes per element for this format.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="format">Texture format</param>
|
|
||||||
/// <returns>Byte size for an element of this format (pixel, vertex attribute, etc)</returns>
|
|
||||||
public static int GetBytesPerElement(this Format format)
|
|
||||||
{
|
|
||||||
int scalarSize = format.GetScalarSize();
|
|
||||||
|
|
||||||
switch (format)
|
|
||||||
{
|
|
||||||
case Format.R8G8Unorm:
|
|
||||||
case Format.R8G8Snorm:
|
|
||||||
case Format.R8G8Uint:
|
|
||||||
case Format.R8G8Sint:
|
|
||||||
case Format.R8G8Uscaled:
|
|
||||||
case Format.R8G8Sscaled:
|
|
||||||
case Format.R16G16Float:
|
|
||||||
case Format.R16G16Unorm:
|
|
||||||
case Format.R16G16Snorm:
|
|
||||||
case Format.R16G16Uint:
|
|
||||||
case Format.R16G16Sint:
|
|
||||||
case Format.R16G16Uscaled:
|
|
||||||
case Format.R16G16Sscaled:
|
|
||||||
case Format.R32G32Float:
|
|
||||||
case Format.R32G32Uint:
|
|
||||||
case Format.R32G32Sint:
|
|
||||||
case Format.R32G32Uscaled:
|
|
||||||
case Format.R32G32Sscaled:
|
|
||||||
return 2 * scalarSize;
|
|
||||||
|
|
||||||
case Format.R8G8B8Unorm:
|
|
||||||
case Format.R8G8B8Snorm:
|
|
||||||
case Format.R8G8B8Uint:
|
|
||||||
case Format.R8G8B8Sint:
|
|
||||||
case Format.R8G8B8Uscaled:
|
|
||||||
case Format.R8G8B8Sscaled:
|
|
||||||
case Format.R16G16B16Float:
|
|
||||||
case Format.R16G16B16Unorm:
|
|
||||||
case Format.R16G16B16Snorm:
|
|
||||||
case Format.R16G16B16Uint:
|
|
||||||
case Format.R16G16B16Sint:
|
|
||||||
case Format.R16G16B16Uscaled:
|
|
||||||
case Format.R16G16B16Sscaled:
|
|
||||||
case Format.R32G32B32Float:
|
|
||||||
case Format.R32G32B32Uint:
|
|
||||||
case Format.R32G32B32Sint:
|
|
||||||
case Format.R32G32B32Uscaled:
|
|
||||||
case Format.R32G32B32Sscaled:
|
|
||||||
return 3 * scalarSize;
|
|
||||||
|
|
||||||
case Format.R8G8B8A8Unorm:
|
|
||||||
case Format.R8G8B8A8Snorm:
|
|
||||||
case Format.R8G8B8A8Uint:
|
|
||||||
case Format.R8G8B8A8Sint:
|
|
||||||
case Format.R8G8B8A8Srgb:
|
|
||||||
case Format.R8G8B8A8Uscaled:
|
|
||||||
case Format.R8G8B8A8Sscaled:
|
|
||||||
case Format.B8G8R8A8Unorm:
|
|
||||||
case Format.B8G8R8A8Srgb:
|
|
||||||
case Format.R16G16B16A16Float:
|
|
||||||
case Format.R16G16B16A16Unorm:
|
|
||||||
case Format.R16G16B16A16Snorm:
|
|
||||||
case Format.R16G16B16A16Uint:
|
|
||||||
case Format.R16G16B16A16Sint:
|
|
||||||
case Format.R16G16B16A16Uscaled:
|
|
||||||
case Format.R16G16B16A16Sscaled:
|
|
||||||
case Format.R32G32B32A32Float:
|
|
||||||
case Format.R32G32B32A32Uint:
|
|
||||||
case Format.R32G32B32A32Sint:
|
|
||||||
case Format.R32G32B32A32Uscaled:
|
|
||||||
case Format.R32G32B32A32Sscaled:
|
|
||||||
return 4 * scalarSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return scalarSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the texture format is a depth or depth-stencil format.
|
/// Checks if the texture format is a depth or depth-stencil format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@ -10,6 +12,20 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
bool PreferThreading { get; }
|
bool PreferThreading { get; }
|
||||||
|
|
||||||
|
public IRenderer TryMakeThreaded(BackendThreading backendThreading = BackendThreading.Auto)
|
||||||
|
{
|
||||||
|
if (backendThreading is BackendThreading.On ||
|
||||||
|
(backendThreading is BackendThreading.Auto && PreferThreading))
|
||||||
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): True");
|
||||||
|
return new ThreadedRenderer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): False");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
IPipeline Pipeline { get; }
|
IPipeline Pipeline { get; }
|
||||||
|
|
||||||
IWindow Window { get; }
|
IWindow Window { get; }
|
||||||
|
@ -4,22 +4,23 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
{
|
{
|
||||||
public int FragmentOutputMap { get; }
|
public int FragmentOutputMap { get; }
|
||||||
public ResourceLayout ResourceLayout { get; }
|
public ResourceLayout ResourceLayout { get; }
|
||||||
public ComputeSize ComputeLocalSize { get; }
|
|
||||||
public ProgramPipelineState? State { get; }
|
public ProgramPipelineState? State { get; }
|
||||||
public bool FromCache { get; set; }
|
public bool FromCache { get; set; }
|
||||||
|
|
||||||
public ShaderInfo(
|
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false)
|
||||||
int fragmentOutputMap,
|
|
||||||
ResourceLayout resourceLayout,
|
|
||||||
ComputeSize computeLocalSize,
|
|
||||||
ProgramPipelineState? state,
|
|
||||||
bool fromCache = false)
|
|
||||||
{
|
{
|
||||||
FragmentOutputMap = fragmentOutputMap;
|
FragmentOutputMap = fragmentOutputMap;
|
||||||
ResourceLayout = resourceLayout;
|
ResourceLayout = resourceLayout;
|
||||||
ComputeLocalSize = computeLocalSize;
|
|
||||||
State = state;
|
State = state;
|
||||||
FromCache = fromCache;
|
FromCache = fromCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
|
||||||
|
{
|
||||||
|
FragmentOutputMap = fragmentOutputMap;
|
||||||
|
ResourceLayout = resourceLayout;
|
||||||
|
State = null;
|
||||||
|
FromCache = fromCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
||||||
|
@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class VtgAsComputeContext : IDisposable
|
class VtgAsComputeContext : IDisposable
|
||||||
{
|
{
|
||||||
|
private const int DummyBufferSize = 16;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
format.GetBytesPerElement(),
|
1,
|
||||||
format,
|
format,
|
||||||
DepthStencilMode.Depth,
|
DepthStencilMode.Depth,
|
||||||
Target.TextureBuffer,
|
Target.TextureBuffer,
|
||||||
@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the range for a dummy 16 bytes buffer, filled with zeros.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Dummy buffer range</returns>
|
||||||
|
public BufferRange GetDummyBufferRange()
|
||||||
|
{
|
||||||
|
if (_dummyBuffer == BufferHandle.Null)
|
||||||
|
{
|
||||||
|
_dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory);
|
||||||
|
_context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferRange(_dummyBuffer, 0, DummyBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||||
|
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||||
|
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vbStride = vertexBuffer.UnpackStride();
|
int vbStride = vertexBuffer.UnpackStride();
|
||||||
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
||||||
|
|
||||||
|
ulong oldVbSize = vbSize;
|
||||||
|
|
||||||
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
||||||
int componentSize = format.GetScalarSize();
|
int componentSize = format.GetScalarSize();
|
||||||
|
|
||||||
@ -340,6 +344,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
return maxOutputVertices / verticesPerPrimitive;
|
return maxOutputVertices / verticesPerPrimitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a dummy buffer as vertex buffer into a buffer texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reservations">Shader resource binding reservations</param>
|
||||||
|
/// <param name="index">Buffer texture index</param>
|
||||||
|
/// <param name="format">Buffer texture format</param>
|
||||||
|
private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format)
|
||||||
|
{
|
||||||
|
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
||||||
|
bufferTexture.SetStorage(_vacContext.GetDummyBufferRange());
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Binds a vertex buffer into a buffer texture.
|
/// Binds a vertex buffer into a buffer texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
Span<Rectangle<int>> scissors =
|
Span<Rectangle<int>> scissors =
|
||||||
[
|
[
|
||||||
new Rectangle<int>(scissorX, scissorY, scissorW, scissorH)
|
new(scissorX, scissorY, scissorW, scissorH)
|
||||||
];
|
];
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetScissors(scissors);
|
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Graphics.Device;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gpu.Memory;
|
|||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
@ -7,7 +7,6 @@ using Ryujinx.Memory;
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Ryujinx.Graphics.Gpu.Engine;
|
using Ryujinx.Graphics.Gpu.Engine;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Shader
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
@ -324,11 +324,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
|
|
||||||
bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
|
bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
|
||||||
|
|
||||||
if (context.Capabilities.Api == TargetApi.Metal)
|
|
||||||
{
|
|
||||||
loadHostCache = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int programIndex = 0;
|
int programIndex = 0;
|
||||||
|
|
||||||
DataEntry entry = new();
|
DataEntry entry = new();
|
||||||
@ -397,8 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
context,
|
context,
|
||||||
shaders,
|
shaders,
|
||||||
specState.PipelineState,
|
specState.PipelineState,
|
||||||
specState.TransformFeedbackDescriptors != null,
|
specState.TransformFeedbackDescriptors != null);
|
||||||
specState.ComputeState.GetLocalSize());
|
|
||||||
|
|
||||||
IProgram hostProgram;
|
IProgram hostProgram;
|
||||||
|
|
||||||
@ -635,10 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Capabilities.Api != TargetApi.Metal)
|
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
|
||||||
{
|
|
||||||
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
@ -367,9 +366,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_context.Capabilities.Api == TargetApi.Metal && _context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay))
|
|
||||||
Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]);
|
|
||||||
|
|
||||||
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);
|
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);
|
||||||
_asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);
|
_asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);
|
||||||
}
|
}
|
||||||
@ -494,12 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
||||||
|
|
||||||
ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState;
|
ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null);
|
||||||
|
|
||||||
ShaderInfoBuilder shaderInfoBuilder = new(
|
|
||||||
_context,
|
|
||||||
compilation.SpecializationState.TransformFeedbackDescriptors != null,
|
|
||||||
computeLocalSize: computeState.GetLocalSize());
|
|
||||||
|
|
||||||
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
||||||
{
|
{
|
||||||
@ -729,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
|
|
||||||
ShaderProgram program = translatorContext.Translate();
|
ShaderProgram program = translatorContext.Translate();
|
||||||
|
|
||||||
CachedShaderStage[] shaders = [new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data)];
|
CachedShaderStage[] shaders = [new(program.Info, shader.Code, shader.Cb1Data)];
|
||||||
|
|
||||||
_compilationQueue.Enqueue(new ProgramCompilation([program], shaders, newSpecState, programIndex, isCompute: true));
|
_compilationQueue.Enqueue(new ProgramCompilation([program], shaders, newSpecState, programIndex, isCompute: true));
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private readonly GpuAccessorState _state;
|
private readonly GpuAccessorState _state;
|
||||||
private readonly int _stageIndex;
|
private readonly int _stageIndex;
|
||||||
private readonly bool _compute;
|
private readonly bool _compute;
|
||||||
private readonly bool _isOpenGL;
|
private readonly bool _isVulkan;
|
||||||
private readonly bool _hasGeometryShader;
|
private readonly bool _hasGeometryShader;
|
||||||
private readonly bool _supportsQuads;
|
private readonly bool _supportsQuads;
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
_channel = channel;
|
_channel = channel;
|
||||||
_state = state;
|
_state = state;
|
||||||
_stageIndex = stageIndex;
|
_stageIndex = stageIndex;
|
||||||
_isOpenGL = context.Capabilities.Api == TargetApi.OpenGL;
|
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
||||||
_hasGeometryShader = hasGeometryShader;
|
_hasGeometryShader = hasGeometryShader;
|
||||||
_supportsQuads = context.Capabilities.SupportsQuads;
|
_supportsQuads = context.Capabilities.SupportsQuads;
|
||||||
|
|
||||||
@ -117,10 +117,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
public GpuGraphicsState QueryGraphicsState()
|
public GpuGraphicsState QueryGraphicsState()
|
||||||
{
|
{
|
||||||
return _state.GraphicsState.CreateShaderGraphicsState(
|
return _state.GraphicsState.CreateShaderGraphicsState(
|
||||||
_isOpenGL,
|
!_isVulkan,
|
||||||
_supportsQuads,
|
_supportsQuads,
|
||||||
_hasGeometryShader,
|
_hasGeometryShader,
|
||||||
!_isOpenGL || _state.GraphicsState.YNegateEnabled);
|
_isVulkan || _state.GraphicsState.YNegateEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
{
|
{
|
||||||
@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Shader
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -63,14 +61,5 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
SharedMemorySize = sharedMemorySize;
|
SharedMemorySize = sharedMemorySize;
|
||||||
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the local group size of the shader in a GAL compatible struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Local group size</returns>
|
|
||||||
public ComputeSize GetLocalSize()
|
|
||||||
{
|
|
||||||
return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private static string GetDiskCachePath()
|
private static string GetDiskCachePath()
|
||||||
{
|
{
|
||||||
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
|
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
|
||||||
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader")
|
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
GpuChannelComputeState computeState,
|
GpuChannelComputeState computeState,
|
||||||
ulong gpuVa)
|
ulong gpuVa)
|
||||||
{
|
{
|
||||||
if (_cpPrograms.TryGetValue(gpuVa, out CachedShaderProgram cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
|
if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
|
||||||
{
|
{
|
||||||
return cpShader;
|
return cpShader;
|
||||||
}
|
}
|
||||||
@ -224,11 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
||||||
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false);
|
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false);
|
||||||
|
|
||||||
ShaderSource[] shaderSourcesArray = [CreateShaderSource(translatedShader.Program)];
|
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
||||||
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(
|
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
|
||||||
_context,
|
|
||||||
translatedShader.Program.Info,
|
|
||||||
computeState.GetLocalSize());
|
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||||
|
|
||||||
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
||||||
@ -255,8 +251,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
channel.TextureManager.UpdateRenderTargets();
|
channel.TextureManager.UpdateRenderTargets();
|
||||||
|
|
||||||
RtControl rtControl = state.RtControl;
|
var rtControl = state.RtControl;
|
||||||
TextureMsaaMode msaaMode = state.RtMsaaMode;
|
var msaaMode = state.RtMsaaMode;
|
||||||
|
|
||||||
pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
|
pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
|
||||||
|
|
||||||
@ -266,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
int rtIndex = rtControl.UnpackPermutationIndex(index);
|
int rtIndex = rtControl.UnpackPermutationIndex(index);
|
||||||
|
|
||||||
RtColorState colorState = state.RtColorState[rtIndex];
|
var colorState = state.RtColorState[rtIndex];
|
||||||
|
|
||||||
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
|
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
|
||||||
{
|
{
|
||||||
@ -311,12 +307,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
ref GpuChannelGraphicsState graphicsState,
|
ref GpuChannelGraphicsState graphicsState,
|
||||||
ShaderAddresses addresses)
|
ShaderAddresses addresses)
|
||||||
{
|
{
|
||||||
if (_gpPrograms.TryGetValue(addresses, out CachedShaderProgram gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
|
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
|
||||||
{
|
{
|
||||||
return gpShaders;
|
return gpShaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out CachedGraphicsGuestCode cachedGuestCode))
|
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode))
|
||||||
{
|
{
|
||||||
_gpPrograms[addresses] = gpShaders;
|
_gpPrograms[addresses] = gpShaders;
|
||||||
return gpShaders;
|
return gpShaders;
|
||||||
@ -369,7 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore);
|
bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore);
|
||||||
|
|
||||||
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
||||||
List<ShaderSource> shaderSources = [];
|
List<ShaderSource> shaderSources = new();
|
||||||
|
|
||||||
TranslatorContext previousStage = null;
|
TranslatorContext previousStage = null;
|
||||||
ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute);
|
ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute);
|
||||||
@ -429,8 +425,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage;
|
TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage;
|
||||||
|
|
||||||
(program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
|
program = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
|
||||||
infoBuilder.AddStageInfoVac(vacInfo);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -535,9 +530,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled)
|
private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled)
|
||||||
{
|
{
|
||||||
ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language);
|
ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language);
|
||||||
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled);
|
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled);
|
||||||
|
|
||||||
return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations());
|
return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -587,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
|
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
|
||||||
{
|
{
|
||||||
TfState tf = state.TfState[i];
|
var tf = state.TfState[i];
|
||||||
|
|
||||||
descs[i] = new TransformFeedbackDescriptor(
|
descs[i] = new TransformFeedbackDescriptor(
|
||||||
tf.BufferIndex,
|
tf.BufferIndex,
|
||||||
@ -693,7 +688,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <returns>The generated translator context</returns>
|
/// <returns>The generated translator context</returns>
|
||||||
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
|
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
|
||||||
{
|
{
|
||||||
TranslationOptions options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
|
var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
|
||||||
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,7 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <returns>The generated translator context</returns>
|
/// <returns>The generated translator context</returns>
|
||||||
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
|
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
|
||||||
{
|
{
|
||||||
TranslationOptions options = CreateTranslationOptions(api, flags);
|
var options = CreateTranslationOptions(api, flags);
|
||||||
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,7 +731,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
||||||
|
|
||||||
MemoryManager memoryManager = channel.MemoryManager;
|
var memoryManager = channel.MemoryManager;
|
||||||
|
|
||||||
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
||||||
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
||||||
@ -774,7 +769,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <returns>Compiled graphics shader code</returns>
|
/// <returns>Compiled graphics shader code</returns>
|
||||||
private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code, bool asCompute)
|
private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code, bool asCompute)
|
||||||
{
|
{
|
||||||
MemoryManager memoryManager = channel.MemoryManager;
|
var memoryManager = channel.MemoryManager;
|
||||||
|
|
||||||
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
|
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
|
||||||
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
||||||
@ -802,7 +797,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
if (address == MemoryManager.PteUnmapped || size == 0)
|
if (address == MemoryManager.PteUnmapped || size == 0)
|
||||||
{
|
{
|
||||||
return [];
|
return Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return memoryManager.Physical.GetSpan(address, size).ToArray();
|
return memoryManager.Physical.GetSpan(address, size).ToArray();
|
||||||
@ -827,20 +822,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates shader translation options with the requested graphics API and flags.
|
/// Creates shader translation options with the requested graphics API and flags.
|
||||||
/// The shader language is chosen based on the current configuration and graphics API.
|
/// The shader language is choosen based on the current configuration and graphics API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="api">Target graphics API</param>
|
/// <param name="api">Target graphics API</param>
|
||||||
/// <param name="flags">Translation flags</param>
|
/// <param name="flags">Translation flags</param>
|
||||||
/// <returns>Translation options</returns>
|
/// <returns>Translation options</returns>
|
||||||
private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
|
private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
|
||||||
{
|
{
|
||||||
TargetLanguage lang = api switch
|
TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
|
||||||
{
|
? TargetLanguage.Spirv
|
||||||
TargetApi.OpenGL => TargetLanguage.Glsl,
|
: TargetLanguage.Glsl;
|
||||||
TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl,
|
|
||||||
TargetApi.Metal => TargetLanguage.Msl,
|
|
||||||
_ => throw new NotImplementedException()
|
|
||||||
};
|
|
||||||
|
|
||||||
return new TranslationOptions(lang, api, flags);
|
return new TranslationOptions(lang, api, flags);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
ResourceStages.Geometry;
|
ResourceStages.Geometry;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly ComputeSize _computeLocalSize;
|
|
||||||
|
|
||||||
private int _fragmentOutputMap;
|
private int _fragmentOutputMap;
|
||||||
|
|
||||||
@ -40,11 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
|
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
|
||||||
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
||||||
/// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
|
/// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
|
||||||
/// <param name="computeLocalSize">Indicates the local thread size for a compute shader</param>
|
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false)
|
||||||
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default)
|
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_computeLocalSize = computeLocalSize;
|
|
||||||
|
|
||||||
_fragmentOutputMap = -1;
|
_fragmentOutputMap = -1;
|
||||||
|
|
||||||
@ -98,7 +95,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
|
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
|
||||||
{
|
{
|
||||||
AddDescriptor(stages, type, setIndex, start, count);
|
AddDescriptor(stages, type, setIndex, start, count);
|
||||||
// AddUsage(stages, type, setIndex, start, count, write);
|
AddUsage(stages, type, setIndex, start, count, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -162,25 +159,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
AddUsage(info.Images, stages, isImage: true);
|
AddUsage(info.Images, stages, isImage: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddStageInfoVac(ShaderProgramInfo info)
|
|
||||||
{
|
|
||||||
ResourceStages stages = info.Stage switch
|
|
||||||
{
|
|
||||||
ShaderStage.Compute => ResourceStages.Compute,
|
|
||||||
ShaderStage.Vertex => ResourceStages.Vertex,
|
|
||||||
ShaderStage.TessellationControl => ResourceStages.TessellationControl,
|
|
||||||
ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
|
|
||||||
ShaderStage.Geometry => ResourceStages.Geometry,
|
|
||||||
ShaderStage.Fragment => ResourceStages.Fragment,
|
|
||||||
_ => ResourceStages.None,
|
|
||||||
};
|
|
||||||
|
|
||||||
AddUsage(info.CBuffers, stages, isStorage: false);
|
|
||||||
AddUsage(info.SBuffers, stages, isStorage: true);
|
|
||||||
AddUsage(info.Textures, stages, isImage: false);
|
|
||||||
AddUsage(info.Images, stages, isImage: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a resource descriptor to the list of descriptors.
|
/// Adds a resource descriptor to the list of descriptors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -383,7 +361,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
|
ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||||
|
|
||||||
return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache);
|
if (pipeline.HasValue)
|
||||||
|
{
|
||||||
|
return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -393,16 +378,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="programs">Shaders from the disk cache</param>
|
/// <param name="programs">Shaders from the disk cache</param>
|
||||||
/// <param name="pipeline">Optional pipeline for background compilation</param>
|
/// <param name="pipeline">Optional pipeline for background compilation</param>
|
||||||
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
||||||
/// <param name="computeLocalSize">Compute local thread size</param>
|
|
||||||
/// <returns>Shader information</returns>
|
/// <returns>Shader information</returns>
|
||||||
public static ShaderInfo BuildForCache(
|
public static ShaderInfo BuildForCache(
|
||||||
GpuContext context,
|
GpuContext context,
|
||||||
IEnumerable<CachedShaderStage> programs,
|
IEnumerable<CachedShaderStage> programs,
|
||||||
ProgramPipelineState? pipeline,
|
ProgramPipelineState? pipeline,
|
||||||
bool tfEnabled,
|
bool tfEnabled)
|
||||||
ComputeSize computeLocalSize)
|
|
||||||
{
|
{
|
||||||
ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize);
|
ShaderInfoBuilder builder = new(context, tfEnabled);
|
||||||
|
|
||||||
foreach (CachedShaderStage program in programs)
|
foreach (CachedShaderStage program in programs)
|
||||||
{
|
{
|
||||||
@ -420,12 +403,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that owns the shader</param>
|
/// <param name="context">GPU context that owns the shader</param>
|
||||||
/// <param name="info">Compute shader information</param>
|
/// <param name="info">Compute shader information</param>
|
||||||
/// <param name="computeLocalSize">Compute local thread size</param>
|
|
||||||
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
|
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
|
||||||
/// <returns>Shader information</returns>
|
/// <returns>Shader information</returns>
|
||||||
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false)
|
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
|
||||||
{
|
{
|
||||||
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize);
|
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false);
|
||||||
|
|
||||||
builder.AddStageInfo(info);
|
builder.AddStageInfo(info);
|
||||||
|
|
||||||
@ -440,11 +422,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
||||||
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
|
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
|
||||||
/// <returns>Shader information</returns>
|
/// <returns>Shader information</returns>
|
||||||
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false)
|
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
|
||||||
{
|
{
|
||||||
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute);
|
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
|
||||||
|
|
||||||
builder.AddStageInfoVac(info2);
|
|
||||||
builder.AddStageInfo(info, vertexAsCompute: true);
|
builder.AddStageInfo(info, vertexAsCompute: true);
|
||||||
|
|
||||||
return builder.Build(null, fromCache);
|
return builder.Build(null, fromCache);
|
||||||
|
@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
}
|
}
|
||||||
|
|
||||||
using ManualResetEvent waitEvent = new(false);
|
using ManualResetEvent waitEvent = new(false);
|
||||||
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set());
|
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, _ => waitEvent.Set());
|
||||||
|
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
|
|
||||||
bool signaled = waitEvent.WaitOne(timeout);
|
bool signaled = waitEvent.WaitOne(timeout);
|
||||||
|
|
||||||
if (!signaled && info != null)
|
if (!signaled)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution...");
|
Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution...");
|
||||||
|
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
interface IAuto
|
|
||||||
{
|
|
||||||
bool HasCommandBufferDependency(CommandBufferScoped cbs);
|
|
||||||
|
|
||||||
void IncrementReferenceCount();
|
|
||||||
void DecrementReferenceCount(int cbIndex);
|
|
||||||
void DecrementReferenceCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IAutoPrivate : IAuto
|
|
||||||
{
|
|
||||||
void AddCommandBufferDependencies(CommandBufferScoped cbs);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class Auto<T> : IAutoPrivate, IDisposable where T : IDisposable
|
|
||||||
{
|
|
||||||
private int _referenceCount;
|
|
||||||
private T _value;
|
|
||||||
|
|
||||||
private readonly BitMap _cbOwnership;
|
|
||||||
private readonly MultiFenceHolder _waitable;
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
private bool _destroyed;
|
|
||||||
|
|
||||||
public Auto(T value)
|
|
||||||
{
|
|
||||||
_referenceCount = 1;
|
|
||||||
_value = value;
|
|
||||||
_cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto(T value, MultiFenceHolder waitable) : this(value)
|
|
||||||
{
|
|
||||||
_waitable = waitable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false)
|
|
||||||
{
|
|
||||||
_waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write);
|
|
||||||
return Get(cbs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetUnsafe()
|
|
||||||
{
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Get(CommandBufferScoped cbs)
|
|
||||||
{
|
|
||||||
if (!_destroyed)
|
|
||||||
{
|
|
||||||
AddCommandBufferDependencies(cbs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasCommandBufferDependency(CommandBufferScoped cbs)
|
|
||||||
{
|
|
||||||
return _cbOwnership.IsSet(cbs.CommandBufferIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasRentedCommandBufferDependency(CommandBufferPool cbp)
|
|
||||||
{
|
|
||||||
return _cbOwnership.AnySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCommandBufferDependencies(CommandBufferScoped cbs)
|
|
||||||
{
|
|
||||||
// We don't want to add a reference to this object to the command buffer
|
|
||||||
// more than once, so if we detect that the command buffer already has ownership
|
|
||||||
// of this object, then we can just return without doing anything else.
|
|
||||||
if (_cbOwnership.Set(cbs.CommandBufferIndex))
|
|
||||||
{
|
|
||||||
if (_waitable != null)
|
|
||||||
{
|
|
||||||
cbs.AddWaitable(_waitable);
|
|
||||||
}
|
|
||||||
|
|
||||||
cbs.AddDependant(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryIncrementReferenceCount()
|
|
||||||
{
|
|
||||||
int lastValue;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
lastValue = _referenceCount;
|
|
||||||
|
|
||||||
if (lastValue == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncrementReferenceCount()
|
|
||||||
{
|
|
||||||
if (Interlocked.Increment(ref _referenceCount) == 1)
|
|
||||||
{
|
|
||||||
Interlocked.Decrement(ref _referenceCount);
|
|
||||||
throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DecrementReferenceCount(int cbIndex)
|
|
||||||
{
|
|
||||||
_cbOwnership.Clear(cbIndex);
|
|
||||||
DecrementReferenceCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DecrementReferenceCount()
|
|
||||||
{
|
|
||||||
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
|
||||||
{
|
|
||||||
_value.Dispose();
|
|
||||||
_value = default;
|
|
||||||
_destroyed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(_referenceCount >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!_disposed)
|
|
||||||
{
|
|
||||||
DecrementReferenceCount();
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class BackgroundResource : IDisposable
|
|
||||||
{
|
|
||||||
private readonly MetalRenderer _renderer;
|
|
||||||
|
|
||||||
private CommandBufferPool _pool;
|
|
||||||
private PersistentFlushBuffer _flushBuffer;
|
|
||||||
|
|
||||||
public BackgroundResource(MetalRenderer renderer)
|
|
||||||
{
|
|
||||||
_renderer = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandBufferPool GetPool()
|
|
||||||
{
|
|
||||||
if (_pool == null)
|
|
||||||
{
|
|
||||||
MTLCommandQueue queue = _renderer.BackgroundQueue;
|
|
||||||
_pool = new CommandBufferPool(queue, true);
|
|
||||||
_pool.Initialize(null); // TODO: Proper encoder factory for background render/compute
|
|
||||||
}
|
|
||||||
|
|
||||||
return _pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PersistentFlushBuffer GetFlushBuffer()
|
|
||||||
{
|
|
||||||
_flushBuffer ??= new PersistentFlushBuffer(_renderer);
|
|
||||||
|
|
||||||
return _flushBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_pool?.Dispose();
|
|
||||||
_flushBuffer?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class BackgroundResources : IDisposable
|
|
||||||
{
|
|
||||||
private readonly MetalRenderer _renderer;
|
|
||||||
|
|
||||||
private readonly Dictionary<Thread, BackgroundResource> _resources;
|
|
||||||
|
|
||||||
public BackgroundResources(MetalRenderer renderer)
|
|
||||||
{
|
|
||||||
_renderer = renderer;
|
|
||||||
|
|
||||||
_resources = new Dictionary<Thread, BackgroundResource>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Cleanup()
|
|
||||||
{
|
|
||||||
lock (_resources)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<Thread, BackgroundResource> tuple in _resources)
|
|
||||||
{
|
|
||||||
if (!tuple.Key.IsAlive)
|
|
||||||
{
|
|
||||||
tuple.Value.Dispose();
|
|
||||||
_resources.Remove(tuple.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BackgroundResource Get()
|
|
||||||
{
|
|
||||||
Thread thread = Thread.CurrentThread;
|
|
||||||
|
|
||||||
lock (_resources)
|
|
||||||
{
|
|
||||||
if (!_resources.TryGetValue(thread, out BackgroundResource resource))
|
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
|
|
||||||
resource = new BackgroundResource(_renderer);
|
|
||||||
|
|
||||||
_resources[thread] = resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
lock (_resources)
|
|
||||||
{
|
|
||||||
foreach (BackgroundResource resource in _resources.Values)
|
|
||||||
{
|
|
||||||
resource.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
readonly struct BitMap
|
|
||||||
{
|
|
||||||
public const int IntSize = 64;
|
|
||||||
|
|
||||||
private const int IntShift = 6;
|
|
||||||
private const int IntMask = IntSize - 1;
|
|
||||||
|
|
||||||
private readonly long[] _masks;
|
|
||||||
|
|
||||||
public BitMap(int count)
|
|
||||||
{
|
|
||||||
_masks = new long[(count + IntMask) / IntSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AnySet()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _masks.Length; i++)
|
|
||||||
{
|
|
||||||
if (_masks[i] != 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSet(int bit)
|
|
||||||
{
|
|
||||||
int wordIndex = bit >> IntShift;
|
|
||||||
int wordBit = bit & IntMask;
|
|
||||||
|
|
||||||
long wordMask = 1L << wordBit;
|
|
||||||
|
|
||||||
return (_masks[wordIndex] & wordMask) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSet(int start, int end)
|
|
||||||
{
|
|
||||||
if (start == end)
|
|
||||||
{
|
|
||||||
return IsSet(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
int startIndex = start >> IntShift;
|
|
||||||
int startBit = start & IntMask;
|
|
||||||
long startMask = -1L << startBit;
|
|
||||||
|
|
||||||
int endIndex = end >> IntShift;
|
|
||||||
int endBit = end & IntMask;
|
|
||||||
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
|
||||||
|
|
||||||
if (startIndex == endIndex)
|
|
||||||
{
|
|
||||||
return (_masks[startIndex] & startMask & endMask) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_masks[startIndex] & startMask) != 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = startIndex + 1; i < endIndex; i++)
|
|
||||||
{
|
|
||||||
if (_masks[i] != 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_masks[endIndex] & endMask) != 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Set(int bit)
|
|
||||||
{
|
|
||||||
int wordIndex = bit >> IntShift;
|
|
||||||
int wordBit = bit & IntMask;
|
|
||||||
|
|
||||||
long wordMask = 1L << wordBit;
|
|
||||||
|
|
||||||
if ((_masks[wordIndex] & wordMask) != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_masks[wordIndex] |= wordMask;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetRange(int start, int end)
|
|
||||||
{
|
|
||||||
if (start == end)
|
|
||||||
{
|
|
||||||
Set(start);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int startIndex = start >> IntShift;
|
|
||||||
int startBit = start & IntMask;
|
|
||||||
long startMask = -1L << startBit;
|
|
||||||
|
|
||||||
int endIndex = end >> IntShift;
|
|
||||||
int endBit = end & IntMask;
|
|
||||||
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
|
||||||
|
|
||||||
if (startIndex == endIndex)
|
|
||||||
{
|
|
||||||
_masks[startIndex] |= startMask & endMask;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_masks[startIndex] |= startMask;
|
|
||||||
|
|
||||||
for (int i = startIndex + 1; i < endIndex; i++)
|
|
||||||
{
|
|
||||||
_masks[i] |= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_masks[endIndex] |= endMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear(int bit)
|
|
||||||
{
|
|
||||||
int wordIndex = bit >> IntShift;
|
|
||||||
int wordBit = bit & IntMask;
|
|
||||||
|
|
||||||
long wordMask = 1L << wordBit;
|
|
||||||
|
|
||||||
_masks[wordIndex] &= ~wordMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _masks.Length; i++)
|
|
||||||
{
|
|
||||||
_masks[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearInt(int start, int end)
|
|
||||||
{
|
|
||||||
for (int i = start; i <= end; i++)
|
|
||||||
{
|
|
||||||
_masks[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,385 +0,0 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class BufferHolder : IDisposable
|
|
||||||
{
|
|
||||||
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
|
|
||||||
|
|
||||||
public int Size { get; }
|
|
||||||
|
|
||||||
private readonly IntPtr _map;
|
|
||||||
private readonly MetalRenderer _renderer;
|
|
||||||
private readonly Pipeline _pipeline;
|
|
||||||
|
|
||||||
private readonly MultiFenceHolder _waitable;
|
|
||||||
private readonly Auto<DisposableBuffer> _buffer;
|
|
||||||
|
|
||||||
private readonly ReaderWriterLockSlim _flushLock;
|
|
||||||
private FenceHolder _flushFence;
|
|
||||||
private int _flushWaiting;
|
|
||||||
|
|
||||||
public BufferHolder(MetalRenderer renderer, Pipeline pipeline, MTLBuffer buffer, int size)
|
|
||||||
{
|
|
||||||
_renderer = renderer;
|
|
||||||
_pipeline = pipeline;
|
|
||||||
_map = buffer.Contents;
|
|
||||||
_waitable = new MultiFenceHolder(size);
|
|
||||||
_buffer = new Auto<DisposableBuffer>(new(buffer), _waitable);
|
|
||||||
|
|
||||||
_flushLock = new ReaderWriterLockSlim();
|
|
||||||
|
|
||||||
Size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(bool isWrite)
|
|
||||||
{
|
|
||||||
if (isWrite)
|
|
||||||
{
|
|
||||||
SignalWrite(0, Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(int offset, int size, bool isWrite)
|
|
||||||
{
|
|
||||||
if (isWrite)
|
|
||||||
{
|
|
||||||
SignalWrite(offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignalWrite(int offset, int size)
|
|
||||||
{
|
|
||||||
if (offset == 0 && size == Size)
|
|
||||||
{
|
|
||||||
_cachedConvertedBuffers.Clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_cachedConvertedBuffers.ClearRange(offset, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearFlushFence()
|
|
||||||
{
|
|
||||||
// Assumes _flushLock is held as writer.
|
|
||||||
|
|
||||||
if (_flushFence != null)
|
|
||||||
{
|
|
||||||
if (_flushWaiting == 0)
|
|
||||||
{
|
|
||||||
_flushFence.Put();
|
|
||||||
}
|
|
||||||
|
|
||||||
_flushFence = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WaitForFlushFence()
|
|
||||||
{
|
|
||||||
if (_flushFence == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If storage has changed, make sure the fence has been reached so that the data is in place.
|
|
||||||
_flushLock.ExitReadLock();
|
|
||||||
_flushLock.EnterWriteLock();
|
|
||||||
|
|
||||||
if (_flushFence != null)
|
|
||||||
{
|
|
||||||
FenceHolder fence = _flushFence;
|
|
||||||
Interlocked.Increment(ref _flushWaiting);
|
|
||||||
|
|
||||||
// Don't wait in the lock.
|
|
||||||
|
|
||||||
_flushLock.ExitWriteLock();
|
|
||||||
|
|
||||||
fence.Wait();
|
|
||||||
|
|
||||||
_flushLock.EnterWriteLock();
|
|
||||||
|
|
||||||
if (Interlocked.Decrement(ref _flushWaiting) == 0)
|
|
||||||
{
|
|
||||||
fence.Put();
|
|
||||||
}
|
|
||||||
|
|
||||||
_flushFence = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assumes the _flushLock is held as reader, returns in same state.
|
|
||||||
_flushLock.ExitWriteLock();
|
|
||||||
_flushLock.EnterReadLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PinnedSpan<byte> GetData(int offset, int size)
|
|
||||||
{
|
|
||||||
_flushLock.EnterReadLock();
|
|
||||||
|
|
||||||
WaitForFlushFence();
|
|
||||||
|
|
||||||
Span<byte> result;
|
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
result = GetDataStorage(offset, size);
|
|
||||||
|
|
||||||
// Need to be careful here, the buffer can't be unmapped while the data is being used.
|
|
||||||
_buffer.IncrementReferenceCount();
|
|
||||||
|
|
||||||
_flushLock.ExitReadLock();
|
|
||||||
|
|
||||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException("The buffer is not mapped");
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe Span<byte> GetDataStorage(int offset, int size)
|
|
||||||
{
|
|
||||||
int mappingSize = Math.Min(size, Size - offset);
|
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
return new Span<byte>((void*)(_map + offset), mappingSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException("The buffer is not mapped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, bool allowCbsWait = true)
|
|
||||||
{
|
|
||||||
int dataSize = Math.Min(data.Length, Size - offset);
|
|
||||||
if (dataSize == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
// If persistently mapped, set the data directly if the buffer is not currently in use.
|
|
||||||
bool isRented = _buffer.HasRentedCommandBufferDependency(_renderer.CommandBufferPool);
|
|
||||||
|
|
||||||
// If the buffer is rented, take a little more time and check if the use overlaps this handle.
|
|
||||||
bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false);
|
|
||||||
|
|
||||||
if (!needsFlush)
|
|
||||||
{
|
|
||||||
WaitForFences(offset, dataSize);
|
|
||||||
|
|
||||||
data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
|
||||||
|
|
||||||
SignalWrite(offset, dataSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cbs != null &&
|
|
||||||
cbs.Value.Encoders.CurrentEncoderType == EncoderType.Render &&
|
|
||||||
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
|
|
||||||
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
|
||||||
{
|
|
||||||
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
|
|
||||||
// This avoids ending and beginning render passes on each buffer data upload.
|
|
||||||
|
|
||||||
cbs = _pipeline.GetPreloadCommandBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowCbsWait)
|
|
||||||
{
|
|
||||||
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool rentCbs = cbs == null;
|
|
||||||
if (rentCbs)
|
|
||||||
{
|
|
||||||
cbs = _renderer.CommandBufferPool.Rent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data))
|
|
||||||
{
|
|
||||||
// Need to do a slow upload.
|
|
||||||
BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize);
|
|
||||||
srcHolder.SetDataUnchecked(0, data);
|
|
||||||
|
|
||||||
Auto<DisposableBuffer> srcBuffer = srcHolder.GetBuffer();
|
|
||||||
Auto<DisposableBuffer> dstBuffer = this.GetBuffer(true);
|
|
||||||
|
|
||||||
Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);
|
|
||||||
|
|
||||||
srcHolder.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rentCbs)
|
|
||||||
{
|
|
||||||
cbs.Value.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetDataUnchecked(int offset, ReadOnlySpan<byte> data)
|
|
||||||
{
|
|
||||||
int dataSize = Math.Min(data.Length, Size - offset);
|
|
||||||
if (dataSize == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDataUnchecked<T>(int offset, ReadOnlySpan<T> data) where T : unmanaged
|
|
||||||
{
|
|
||||||
SetDataUnchecked(offset, MemoryMarshal.AsBytes(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Copy(
|
|
||||||
CommandBufferScoped cbs,
|
|
||||||
Auto<DisposableBuffer> src,
|
|
||||||
Auto<DisposableBuffer> dst,
|
|
||||||
int srcOffset,
|
|
||||||
int dstOffset,
|
|
||||||
int size,
|
|
||||||
bool registerSrcUsage = true)
|
|
||||||
{
|
|
||||||
MTLBuffer srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
|
|
||||||
MTLBuffer dstbuffer = dst.Get(cbs, dstOffset, size, true).Value;
|
|
||||||
|
|
||||||
cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer(
|
|
||||||
srcBuffer,
|
|
||||||
(ulong)srcOffset,
|
|
||||||
dstbuffer,
|
|
||||||
(ulong)dstOffset,
|
|
||||||
(ulong)size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitForFences()
|
|
||||||
{
|
|
||||||
_waitable.WaitForFences();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitForFences(int offset, int size)
|
|
||||||
{
|
|
||||||
_waitable.WaitForFences(offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool BoundToRange(int offset, ref int size)
|
|
||||||
{
|
|
||||||
if (offset >= Size)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size = Math.Min(Size - offset, size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
|
|
||||||
{
|
|
||||||
if (!BoundToRange(offset, ref size))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
I8ToI16CacheKey key = new(_renderer);
|
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
holder = _renderer.BufferManager.Create((size * 2 + 3) & ~3);
|
|
||||||
|
|
||||||
_renderer.HelperShader.ConvertI8ToI16(cbs, this, holder, offset, size);
|
|
||||||
|
|
||||||
key.SetBuffer(holder.GetBuffer());
|
|
||||||
|
|
||||||
_cachedConvertedBuffers.Add(offset, size, key, holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return holder.GetBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
|
||||||
{
|
|
||||||
if (!BoundToRange(offset, ref size))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TopologyConversionCacheKey key = new(_renderer, pattern, indexSize);
|
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
// The destination index size is always I32.
|
|
||||||
|
|
||||||
int indexCount = size / indexSize;
|
|
||||||
|
|
||||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
|
||||||
|
|
||||||
holder = _renderer.BufferManager.Create(convertedCount * 4);
|
|
||||||
|
|
||||||
_renderer.HelperShader.ConvertIndexBuffer(cbs, this, holder, pattern, indexSize, offset, indexCount);
|
|
||||||
|
|
||||||
key.SetBuffer(holder.GetBuffer());
|
|
||||||
|
|
||||||
_cachedConvertedBuffers.Add(offset, size, key, holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return holder.GetBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder)
|
|
||||||
{
|
|
||||||
return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder)
|
|
||||||
{
|
|
||||||
_cachedConvertedBuffers.Add(offset, size, key, holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency)
|
|
||||||
{
|
|
||||||
_cachedConvertedBuffers.AddDependency(offset, size, key, dependency);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key)
|
|
||||||
{
|
|
||||||
_cachedConvertedBuffers.Remove(offset, size, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
|
|
||||||
|
|
||||||
_buffer.Dispose();
|
|
||||||
_cachedConvertedBuffers.Dispose();
|
|
||||||
|
|
||||||
_flushLock.EnterWriteLock();
|
|
||||||
|
|
||||||
ClearFlushFence();
|
|
||||||
|
|
||||||
_flushLock.ExitWriteLock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,237 +0,0 @@
|
|||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
readonly struct ScopedTemporaryBuffer : IDisposable
|
|
||||||
{
|
|
||||||
private readonly BufferManager _bufferManager;
|
|
||||||
private readonly bool _isReserved;
|
|
||||||
|
|
||||||
public readonly BufferRange Range;
|
|
||||||
public readonly BufferHolder Holder;
|
|
||||||
|
|
||||||
public BufferHandle Handle => Range.Handle;
|
|
||||||
public int Offset => Range.Offset;
|
|
||||||
|
|
||||||
public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved)
|
|
||||||
{
|
|
||||||
_bufferManager = bufferManager;
|
|
||||||
|
|
||||||
Range = new BufferRange(handle, offset, size);
|
|
||||||
Holder = holder;
|
|
||||||
|
|
||||||
_isReserved = isReserved;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!_isReserved)
|
|
||||||
{
|
|
||||||
_bufferManager.Delete(Range.Handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class BufferManager : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IdList<BufferHolder> _buffers;
|
|
||||||
|
|
||||||
private readonly MTLDevice _device;
|
|
||||||
private readonly MetalRenderer _renderer;
|
|
||||||
private readonly Pipeline _pipeline;
|
|
||||||
|
|
||||||
public int BufferCount { get; private set; }
|
|
||||||
|
|
||||||
public StagingBuffer StagingBuffer { get; }
|
|
||||||
|
|
||||||
public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
|
|
||||||
{
|
|
||||||
_device = device;
|
|
||||||
_renderer = renderer;
|
|
||||||
_pipeline = pipeline;
|
|
||||||
_buffers = new IdList<BufferHolder>();
|
|
||||||
|
|
||||||
StagingBuffer = new StagingBuffer(_renderer, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferHandle Create(nint pointer, int size)
|
|
||||||
{
|
|
||||||
// TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us.
|
|
||||||
MTLBuffer buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared);
|
|
||||||
|
|
||||||
if (buffer == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}.");
|
|
||||||
|
|
||||||
return BufferHandle.Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferHolder holder = new(_renderer, _pipeline, buffer, size);
|
|
||||||
|
|
||||||
BufferCount++;
|
|
||||||
|
|
||||||
ulong handle64 = (uint)_buffers.Add(holder);
|
|
||||||
|
|
||||||
return Unsafe.As<ulong, BufferHandle>(ref handle64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(int size)
|
|
||||||
{
|
|
||||||
return CreateWithHandle(size, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(int size, out BufferHolder holder)
|
|
||||||
{
|
|
||||||
holder = Create(size);
|
|
||||||
|
|
||||||
if (holder == null)
|
|
||||||
{
|
|
||||||
return BufferHandle.Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferCount++;
|
|
||||||
|
|
||||||
ulong handle64 = (uint)_buffers.Add(holder);
|
|
||||||
|
|
||||||
return Unsafe.As<ulong, BufferHandle>(ref handle64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size)
|
|
||||||
{
|
|
||||||
StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size);
|
|
||||||
|
|
||||||
if (result.HasValue)
|
|
||||||
{
|
|
||||||
return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create a temporary buffer.
|
|
||||||
BufferHandle handle = CreateWithHandle(size, out BufferHolder holder);
|
|
||||||
|
|
||||||
return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferHolder Create(int size)
|
|
||||||
{
|
|
||||||
MTLBuffer buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared);
|
|
||||||
|
|
||||||
if (buffer != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
return new BufferHolder(_renderer, _pipeline, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite, out int size)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
size = holder.Size;
|
|
||||||
return holder.GetBuffer(isWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
size = 0;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, int offset, int size, bool isWrite)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
return holder.GetBuffer(offset, size, isWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
return holder.GetBuffer(isWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
return holder.GetBufferI8ToI16(cbs, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
return holder.GetData(offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PinnedSpan<byte>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
|
||||||
{
|
|
||||||
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
holder.SetData(offset, data, cbs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Delete(BufferHandle handle)
|
|
||||||
{
|
|
||||||
if (TryGetBuffer(handle, out BufferHolder holder))
|
|
||||||
{
|
|
||||||
holder.Dispose();
|
|
||||||
_buffers.Remove((int)Unsafe.As<BufferHandle, ulong>(ref handle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder)
|
|
||||||
{
|
|
||||||
return _buffers.TryGetValue((int)Unsafe.As<BufferHandle, ulong>(ref handle), out holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
StagingBuffer.Dispose();
|
|
||||||
|
|
||||||
foreach (BufferHolder buffer in _buffers)
|
|
||||||
{
|
|
||||||
buffer.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
internal class BufferUsageBitmap
|
|
||||||
{
|
|
||||||
private readonly BitMap _bitmap;
|
|
||||||
private readonly int _size;
|
|
||||||
private readonly int _granularity;
|
|
||||||
private readonly int _bits;
|
|
||||||
private readonly int _writeBitOffset;
|
|
||||||
|
|
||||||
private readonly int _intsPerCb;
|
|
||||||
private readonly int _bitsPerCb;
|
|
||||||
|
|
||||||
public BufferUsageBitmap(int size, int granularity)
|
|
||||||
{
|
|
||||||
_size = size;
|
|
||||||
_granularity = granularity;
|
|
||||||
|
|
||||||
// There are two sets of bits - one for read tracking, and the other for write.
|
|
||||||
int bits = (size + (granularity - 1)) / granularity;
|
|
||||||
_writeBitOffset = bits;
|
|
||||||
_bits = bits << 1;
|
|
||||||
|
|
||||||
_intsPerCb = (_bits + (BitMap.IntSize - 1)) / BitMap.IntSize;
|
|
||||||
_bitsPerCb = _intsPerCb * BitMap.IntSize;
|
|
||||||
|
|
||||||
_bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(int cbIndex, int offset, int size, bool write)
|
|
||||||
{
|
|
||||||
if (size == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some usages can be out of bounds (vertex buffer on amd), so bound if necessary.
|
|
||||||
if (offset + size > _size)
|
|
||||||
{
|
|
||||||
size = _size - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0);
|
|
||||||
int start = cbBase + offset / _granularity;
|
|
||||||
int end = cbBase + (offset + size - 1) / _granularity;
|
|
||||||
|
|
||||||
_bitmap.SetRange(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false)
|
|
||||||
{
|
|
||||||
if (size == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0);
|
|
||||||
int start = cbBase + offset / _granularity;
|
|
||||||
int end = cbBase + (offset + size - 1) / _granularity;
|
|
||||||
|
|
||||||
return _bitmap.IsSet(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OverlapsWith(int offset, int size, bool write)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
if (OverlapsWith(i, offset, size, write))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear(int cbIndex)
|
|
||||||
{
|
|
||||||
_bitmap.ClearInt(cbIndex * _intsPerCb, (cbIndex + 1) * _intsPerCb - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,294 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
interface ICacheKey : IDisposable
|
|
||||||
{
|
|
||||||
bool KeyEqual(ICacheKey other);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
struct I8ToI16CacheKey : ICacheKey
|
|
||||||
{
|
|
||||||
// Used to notify the pipeline that bindings have invalidated on dispose.
|
|
||||||
// private readonly MetalRenderer _renderer;
|
|
||||||
// private Auto<DisposableBuffer> _buffer;
|
|
||||||
|
|
||||||
public I8ToI16CacheKey(MetalRenderer renderer)
|
|
||||||
{
|
|
||||||
// _renderer = renderer;
|
|
||||||
// _buffer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool KeyEqual(ICacheKey other)
|
|
||||||
{
|
|
||||||
return other is I8ToI16CacheKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void SetBuffer(Auto<DisposableBuffer> buffer)
|
|
||||||
{
|
|
||||||
// _buffer = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void Dispose()
|
|
||||||
{
|
|
||||||
// TODO: Tell pipeline buffer is dirty!
|
|
||||||
// _renderer.PipelineInternal.DirtyIndexBuffer(_buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
readonly struct TopologyConversionCacheKey : ICacheKey
|
|
||||||
{
|
|
||||||
private readonly IndexBufferPattern _pattern;
|
|
||||||
private readonly int _indexSize;
|
|
||||||
|
|
||||||
// Used to notify the pipeline that bindings have invalidated on dispose.
|
|
||||||
// private readonly MetalRenderer _renderer;
|
|
||||||
// private Auto<DisposableBuffer> _buffer;
|
|
||||||
|
|
||||||
public TopologyConversionCacheKey(MetalRenderer renderer, IndexBufferPattern pattern, int indexSize)
|
|
||||||
{
|
|
||||||
// _renderer = renderer;
|
|
||||||
// _buffer = null;
|
|
||||||
_pattern = pattern;
|
|
||||||
_indexSize = indexSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool KeyEqual(ICacheKey other)
|
|
||||||
{
|
|
||||||
return other is TopologyConversionCacheKey entry &&
|
|
||||||
entry._pattern == _pattern &&
|
|
||||||
entry._indexSize == _indexSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBuffer(Auto<DisposableBuffer> buffer)
|
|
||||||
{
|
|
||||||
// _buffer = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void Dispose()
|
|
||||||
{
|
|
||||||
// TODO: Tell pipeline buffer is dirty!
|
|
||||||
// _renderer.PipelineInternal.DirtyVertexBuffer(_buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
readonly struct Dependency
|
|
||||||
{
|
|
||||||
private readonly BufferHolder _buffer;
|
|
||||||
private readonly int _offset;
|
|
||||||
private readonly int _size;
|
|
||||||
private readonly ICacheKey _key;
|
|
||||||
|
|
||||||
public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key)
|
|
||||||
{
|
|
||||||
_buffer = buffer;
|
|
||||||
_offset = offset;
|
|
||||||
_size = size;
|
|
||||||
_key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveFromOwner()
|
|
||||||
{
|
|
||||||
_buffer.RemoveCachedConvertedBuffer(_offset, _size, _key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
struct CacheByRange<T> where T : IDisposable
|
|
||||||
{
|
|
||||||
private struct Entry
|
|
||||||
{
|
|
||||||
public readonly ICacheKey Key;
|
|
||||||
public readonly T Value;
|
|
||||||
public List<Dependency> DependencyList;
|
|
||||||
|
|
||||||
public Entry(ICacheKey key, T value)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
Value = value;
|
|
||||||
DependencyList = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void InvalidateDependencies()
|
|
||||||
{
|
|
||||||
if (DependencyList != null)
|
|
||||||
{
|
|
||||||
foreach (Dependency dependency in DependencyList)
|
|
||||||
{
|
|
||||||
dependency.RemoveFromOwner();
|
|
||||||
}
|
|
||||||
|
|
||||||
DependencyList.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<ulong, List<Entry>> _ranges;
|
|
||||||
|
|
||||||
public void Add(int offset, int size, ICacheKey key, T value)
|
|
||||||
{
|
|
||||||
List<Entry> entries = GetEntries(offset, size);
|
|
||||||
|
|
||||||
entries.Add(new Entry(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency)
|
|
||||||
{
|
|
||||||
List<Entry> entries = GetEntries(offset, size);
|
|
||||||
|
|
||||||
for (int i = 0; i < entries.Count; i++)
|
|
||||||
{
|
|
||||||
Entry entry = entries[i];
|
|
||||||
|
|
||||||
if (entry.Key.KeyEqual(key))
|
|
||||||
{
|
|
||||||
if (entry.DependencyList == null)
|
|
||||||
{
|
|
||||||
entry.DependencyList = [];
|
|
||||||
entries[i] = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.DependencyList.Add(dependency);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(int offset, int size, ICacheKey key)
|
|
||||||
{
|
|
||||||
List<Entry> entries = GetEntries(offset, size);
|
|
||||||
|
|
||||||
for (int i = 0; i < entries.Count; i++)
|
|
||||||
{
|
|
||||||
Entry entry = entries[i];
|
|
||||||
|
|
||||||
if (entry.Key.KeyEqual(key))
|
|
||||||
{
|
|
||||||
entries.RemoveAt(i--);
|
|
||||||
|
|
||||||
DestroyEntry(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entries.Count == 0)
|
|
||||||
{
|
|
||||||
_ranges.Remove(PackRange(offset, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
|
|
||||||
{
|
|
||||||
List<Entry> entries = GetEntries(offset, size);
|
|
||||||
|
|
||||||
foreach (Entry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Key.KeyEqual(key))
|
|
||||||
{
|
|
||||||
value = entry.Value;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_ranges != null)
|
|
||||||
{
|
|
||||||
foreach (List<Entry> entries in _ranges.Values)
|
|
||||||
{
|
|
||||||
foreach (Entry entry in entries)
|
|
||||||
{
|
|
||||||
DestroyEntry(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ranges.Clear();
|
|
||||||
_ranges = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void ClearRange(int offset, int size)
|
|
||||||
{
|
|
||||||
if (_ranges != null && _ranges.Count > 0)
|
|
||||||
{
|
|
||||||
int end = offset + size;
|
|
||||||
|
|
||||||
List<ulong> toRemove = null;
|
|
||||||
|
|
||||||
foreach (KeyValuePair<ulong, List<Entry>> range in _ranges)
|
|
||||||
{
|
|
||||||
(int rOffset, int rSize) = UnpackRange(range.Key);
|
|
||||||
|
|
||||||
int rEnd = rOffset + rSize;
|
|
||||||
|
|
||||||
if (rEnd > offset && rOffset < end)
|
|
||||||
{
|
|
||||||
List<Entry> entries = range.Value;
|
|
||||||
|
|
||||||
foreach (Entry entry in entries)
|
|
||||||
{
|
|
||||||
DestroyEntry(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
(toRemove ??= []).Add(range.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toRemove != null)
|
|
||||||
{
|
|
||||||
foreach (ulong range in toRemove)
|
|
||||||
{
|
|
||||||
_ranges.Remove(range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Entry> GetEntries(int offset, int size)
|
|
||||||
{
|
|
||||||
_ranges ??= new Dictionary<ulong, List<Entry>>();
|
|
||||||
|
|
||||||
ulong key = PackRange(offset, size);
|
|
||||||
|
|
||||||
if (!_ranges.TryGetValue(key, out List<Entry> value))
|
|
||||||
{
|
|
||||||
value = [];
|
|
||||||
_ranges.Add(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DestroyEntry(Entry entry)
|
|
||||||
{
|
|
||||||
entry.Key.Dispose();
|
|
||||||
entry.Value?.Dispose();
|
|
||||||
entry.InvalidateDependencies();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ulong PackRange(int offset, int size)
|
|
||||||
{
|
|
||||||
return (uint)offset | ((ulong)size << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int offset, int size) UnpackRange(ulong range)
|
|
||||||
{
|
|
||||||
return ((int)range, (int)(range >> 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Metal;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
interface IEncoderFactory
|
|
||||||
{
|
|
||||||
MTLRenderCommandEncoder CreateRenderCommandEncoder();
|
|
||||||
MTLComputeCommandEncoder CreateComputeCommandEncoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tracks active encoder object for a command buffer.
|
|
||||||
/// </summary>
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class CommandBufferEncoder
|
|
||||||
{
|
|
||||||
public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None;
|
|
||||||
|
|
||||||
public MTLBlitCommandEncoder BlitEncoder => new(CurrentEncoder.Value);
|
|
||||||
|
|
||||||
public MTLComputeCommandEncoder ComputeEncoder => new(CurrentEncoder.Value);
|
|
||||||
|
|
||||||
public MTLRenderCommandEncoder RenderEncoder => new(CurrentEncoder.Value);
|
|
||||||
|
|
||||||
internal MTLCommandEncoder? CurrentEncoder { get; private set; }
|
|
||||||
|
|
||||||
private MTLCommandBuffer _commandBuffer;
|
|
||||||
private IEncoderFactory _encoderFactory;
|
|
||||||
|
|
||||||
public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory)
|
|
||||||
{
|
|
||||||
_commandBuffer = commandBuffer;
|
|
||||||
_encoderFactory = encoderFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public MTLRenderCommandEncoder EnsureRenderEncoder()
|
|
||||||
{
|
|
||||||
if (CurrentEncoderType != EncoderType.Render)
|
|
||||||
{
|
|
||||||
return BeginRenderPass();
|
|
||||||
}
|
|
||||||
|
|
||||||
return RenderEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public MTLBlitCommandEncoder EnsureBlitEncoder()
|
|
||||||
{
|
|
||||||
if (CurrentEncoderType != EncoderType.Blit)
|
|
||||||
{
|
|
||||||
return BeginBlitPass();
|
|
||||||
}
|
|
||||||
|
|
||||||
return BlitEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public MTLComputeCommandEncoder EnsureComputeEncoder()
|
|
||||||
{
|
|
||||||
if (CurrentEncoderType != EncoderType.Compute)
|
|
||||||
{
|
|
||||||
return BeginComputePass();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComputeEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder)
|
|
||||||
{
|
|
||||||
if (CurrentEncoderType != EncoderType.Render)
|
|
||||||
{
|
|
||||||
encoder = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder = RenderEncoder;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder)
|
|
||||||
{
|
|
||||||
if (CurrentEncoderType != EncoderType.Blit)
|
|
||||||
{
|
|
||||||
encoder = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder = BlitEncoder;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder)
|
|
||||||
{
|
|
||||||
if (CurrentEncoderType != EncoderType.Compute)
|
|
||||||
{
|
|
||||||
encoder = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder = ComputeEncoder;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndCurrentPass()
|
|
||||||
{
|
|
||||||
if (CurrentEncoder != null)
|
|
||||||
{
|
|
||||||
switch (CurrentEncoderType)
|
|
||||||
{
|
|
||||||
case EncoderType.Blit:
|
|
||||||
BlitEncoder.EndEncoding();
|
|
||||||
CurrentEncoder = null;
|
|
||||||
break;
|
|
||||||
case EncoderType.Compute:
|
|
||||||
ComputeEncoder.EndEncoding();
|
|
||||||
CurrentEncoder = null;
|
|
||||||
break;
|
|
||||||
case EncoderType.Render:
|
|
||||||
RenderEncoder.EndEncoding();
|
|
||||||
CurrentEncoder = null;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentEncoderType = EncoderType.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MTLRenderCommandEncoder BeginRenderPass()
|
|
||||||
{
|
|
||||||
EndCurrentPass();
|
|
||||||
|
|
||||||
MTLRenderCommandEncoder renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder();
|
|
||||||
|
|
||||||
CurrentEncoder = renderCommandEncoder;
|
|
||||||
CurrentEncoderType = EncoderType.Render;
|
|
||||||
|
|
||||||
return renderCommandEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MTLBlitCommandEncoder BeginBlitPass()
|
|
||||||
{
|
|
||||||
EndCurrentPass();
|
|
||||||
|
|
||||||
using MTLBlitPassDescriptor descriptor = new();
|
|
||||||
MTLBlitCommandEncoder blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor);
|
|
||||||
|
|
||||||
CurrentEncoder = blitCommandEncoder;
|
|
||||||
CurrentEncoderType = EncoderType.Blit;
|
|
||||||
return blitCommandEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MTLComputeCommandEncoder BeginComputePass()
|
|
||||||
{
|
|
||||||
EndCurrentPass();
|
|
||||||
|
|
||||||
MTLComputeCommandEncoder computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder();
|
|
||||||
|
|
||||||
CurrentEncoder = computeCommandEncoder;
|
|
||||||
CurrentEncoderType = EncoderType.Compute;
|
|
||||||
return computeCommandEncoder;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,289 +0,0 @@
|
|||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class CommandBufferPool : IDisposable
|
|
||||||
{
|
|
||||||
public const int MaxCommandBuffers = 16;
|
|
||||||
|
|
||||||
private readonly int _totalCommandBuffers;
|
|
||||||
private readonly int _totalCommandBuffersMask;
|
|
||||||
private readonly MTLCommandQueue _queue;
|
|
||||||
private readonly Thread _owner;
|
|
||||||
private IEncoderFactory _defaultEncoderFactory;
|
|
||||||
|
|
||||||
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
private struct ReservedCommandBuffer
|
|
||||||
{
|
|
||||||
public bool InUse;
|
|
||||||
public bool InConsumption;
|
|
||||||
public int SubmissionCount;
|
|
||||||
public MTLCommandBuffer CommandBuffer;
|
|
||||||
public CommandBufferEncoder Encoders;
|
|
||||||
public FenceHolder Fence;
|
|
||||||
|
|
||||||
public List<IAuto> Dependants;
|
|
||||||
public List<MultiFenceHolder> Waitables;
|
|
||||||
|
|
||||||
public void Use(MTLCommandQueue queue, IEncoderFactory stateManager)
|
|
||||||
{
|
|
||||||
MTLCommandBufferDescriptor descriptor = new();
|
|
||||||
#if DEBUG
|
|
||||||
descriptor.ErrorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CommandBuffer = queue.CommandBuffer(descriptor);
|
|
||||||
Fence = new FenceHolder(CommandBuffer);
|
|
||||||
|
|
||||||
Encoders.Initialize(CommandBuffer, stateManager);
|
|
||||||
|
|
||||||
InUse = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
Dependants = [];
|
|
||||||
Waitables = [];
|
|
||||||
Encoders = new CommandBufferEncoder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ReservedCommandBuffer[] _commandBuffers;
|
|
||||||
|
|
||||||
private readonly int[] _queuedIndexes;
|
|
||||||
private int _queuedIndexesPtr;
|
|
||||||
private int _queuedCount;
|
|
||||||
private int _inUseCount;
|
|
||||||
|
|
||||||
public CommandBufferPool(MTLCommandQueue queue, bool isLight = false)
|
|
||||||
{
|
|
||||||
_queue = queue;
|
|
||||||
_owner = Thread.CurrentThread;
|
|
||||||
|
|
||||||
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
|
|
||||||
_totalCommandBuffersMask = _totalCommandBuffers - 1;
|
|
||||||
|
|
||||||
_commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers];
|
|
||||||
|
|
||||||
_queuedIndexes = new int[_totalCommandBuffers];
|
|
||||||
_queuedIndexesPtr = 0;
|
|
||||||
_queuedCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize(IEncoderFactory encoderFactory)
|
|
||||||
{
|
|
||||||
_defaultEncoderFactory = encoderFactory;
|
|
||||||
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
_commandBuffers[i].Initialize();
|
|
||||||
WaitAndDecrementRef(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddDependant(int cbIndex, IAuto dependant)
|
|
||||||
{
|
|
||||||
dependant.IncrementReferenceCount();
|
|
||||||
_commandBuffers[cbIndex].Dependants.Add(dependant);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddWaitable(MultiFenceHolder waitable)
|
|
||||||
{
|
|
||||||
lock (_commandBuffers)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
|
|
||||||
|
|
||||||
if (entry.InConsumption)
|
|
||||||
{
|
|
||||||
AddWaitable(i, waitable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddInUseWaitable(MultiFenceHolder waitable)
|
|
||||||
{
|
|
||||||
lock (_commandBuffers)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
|
|
||||||
|
|
||||||
if (entry.InUse)
|
|
||||||
{
|
|
||||||
AddWaitable(i, waitable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
|
|
||||||
{
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
|
|
||||||
if (waitable.AddFence(cbIndex, entry.Fence))
|
|
||||||
{
|
|
||||||
entry.Waitables.Add(waitable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFenceOnRentedCommandBuffer(FenceHolder fence)
|
|
||||||
{
|
|
||||||
lock (_commandBuffers)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
|
|
||||||
|
|
||||||
if (entry.InUse && entry.Fence == fence)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FenceHolder GetFence(int cbIndex)
|
|
||||||
{
|
|
||||||
return _commandBuffers[cbIndex].Fence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetSubmissionCount(int cbIndex)
|
|
||||||
{
|
|
||||||
return _commandBuffers[cbIndex].SubmissionCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int FreeConsumed(bool wait)
|
|
||||||
{
|
|
||||||
int freeEntry = 0;
|
|
||||||
|
|
||||||
while (_queuedCount > 0)
|
|
||||||
{
|
|
||||||
int index = _queuedIndexes[_queuedIndexesPtr];
|
|
||||||
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[index];
|
|
||||||
|
|
||||||
if (wait || !entry.InConsumption || entry.Fence.IsSignaled())
|
|
||||||
{
|
|
||||||
WaitAndDecrementRef(index);
|
|
||||||
|
|
||||||
wait = false;
|
|
||||||
freeEntry = index;
|
|
||||||
|
|
||||||
_queuedCount--;
|
|
||||||
_queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return freeEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
|
|
||||||
{
|
|
||||||
Return(cbs);
|
|
||||||
return Rent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandBufferScoped Rent()
|
|
||||||
{
|
|
||||||
lock (_commandBuffers)
|
|
||||||
{
|
|
||||||
int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers);
|
|
||||||
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[cursor];
|
|
||||||
|
|
||||||
if (!entry.InUse && !entry.InConsumption)
|
|
||||||
{
|
|
||||||
entry.Use(_queue, _defaultEncoderFactory);
|
|
||||||
|
|
||||||
_inUseCount++;
|
|
||||||
|
|
||||||
return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor = (cursor + 1) & _totalCommandBuffersMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Out of command buffers (In use: {_inUseCount}, queued: {_queuedCount}, total: {_totalCommandBuffers})");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Return(CommandBufferScoped cbs)
|
|
||||||
{
|
|
||||||
// Ensure the encoder is committed.
|
|
||||||
cbs.Encoders.EndCurrentPass();
|
|
||||||
|
|
||||||
lock (_commandBuffers)
|
|
||||||
{
|
|
||||||
int cbIndex = cbs.CommandBufferIndex;
|
|
||||||
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
|
|
||||||
|
|
||||||
Debug.Assert(entry.InUse);
|
|
||||||
Debug.Assert(entry.CommandBuffer.NativePtr == cbs.CommandBuffer.NativePtr);
|
|
||||||
entry.InUse = false;
|
|
||||||
entry.InConsumption = true;
|
|
||||||
entry.SubmissionCount++;
|
|
||||||
_inUseCount--;
|
|
||||||
|
|
||||||
MTLCommandBuffer commandBuffer = entry.CommandBuffer;
|
|
||||||
commandBuffer.Commit();
|
|
||||||
|
|
||||||
int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
|
|
||||||
_queuedIndexes[ptr] = cbIndex;
|
|
||||||
_queuedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WaitAndDecrementRef(int cbIndex)
|
|
||||||
{
|
|
||||||
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
|
|
||||||
|
|
||||||
if (entry.InConsumption)
|
|
||||||
{
|
|
||||||
entry.Fence.Wait();
|
|
||||||
entry.InConsumption = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (IAuto dependant in entry.Dependants)
|
|
||||||
{
|
|
||||||
dependant.DecrementReferenceCount(cbIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (MultiFenceHolder waitable in entry.Waitables)
|
|
||||||
{
|
|
||||||
waitable.RemoveFence(cbIndex);
|
|
||||||
waitable.RemoveBufferUses(cbIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Dependants.Clear();
|
|
||||||
entry.Waitables.Clear();
|
|
||||||
entry.Fence?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
|
||||||
{
|
|
||||||
WaitAndDecrementRef(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
readonly struct CommandBufferScoped : IDisposable
|
|
||||||
{
|
|
||||||
private readonly CommandBufferPool _pool;
|
|
||||||
public MTLCommandBuffer CommandBuffer { get; }
|
|
||||||
public CommandBufferEncoder Encoders { get; }
|
|
||||||
public int CommandBufferIndex { get; }
|
|
||||||
|
|
||||||
public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex)
|
|
||||||
{
|
|
||||||
_pool = pool;
|
|
||||||
CommandBuffer = commandBuffer;
|
|
||||||
Encoders = encoders;
|
|
||||||
CommandBufferIndex = commandBufferIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddDependant(IAuto dependant)
|
|
||||||
{
|
|
||||||
_pool.AddDependant(CommandBufferIndex, dependant);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddWaitable(MultiFenceHolder waitable)
|
|
||||||
{
|
|
||||||
_pool.AddWaitable(CommandBufferIndex, waitable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FenceHolder GetFence()
|
|
||||||
{
|
|
||||||
return _pool.GetFence(CommandBufferIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_pool?.Return(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
static class Constants
|
|
||||||
{
|
|
||||||
public const int MaxShaderStages = 5;
|
|
||||||
public const int MaxVertexBuffers = 16;
|
|
||||||
public const int MaxUniformBuffersPerStage = 18;
|
|
||||||
public const int MaxStorageBuffersPerStage = 16;
|
|
||||||
public const int MaxTexturesPerStage = 64;
|
|
||||||
public const int MaxImagesPerStage = 16;
|
|
||||||
|
|
||||||
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
|
|
||||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
|
||||||
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
|
|
||||||
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
|
|
||||||
public const int MaxColorAttachments = 8;
|
|
||||||
public const int MaxViewports = 16;
|
|
||||||
// TODO: Check this value
|
|
||||||
public const int MaxVertexAttributes = 31;
|
|
||||||
|
|
||||||
public const int MinResourceAlignment = 16;
|
|
||||||
|
|
||||||
// Must match constants set in shader generation
|
|
||||||
public const uint ZeroBufferIndex = MaxVertexBuffers;
|
|
||||||
public const uint BaseSetIndex = MaxVertexBuffers + 1;
|
|
||||||
|
|
||||||
public const uint ConstantBuffersIndex = BaseSetIndex;
|
|
||||||
public const uint StorageBuffersIndex = BaseSetIndex + 1;
|
|
||||||
public const uint TexturesIndex = BaseSetIndex + 2;
|
|
||||||
public const uint ImagesIndex = BaseSetIndex + 3;
|
|
||||||
|
|
||||||
public const uint ConstantBuffersSetIndex = 0;
|
|
||||||
public const uint StorageBuffersSetIndex = 1;
|
|
||||||
public const uint TexturesSetIndex = 2;
|
|
||||||
public const uint ImagesSetIndex = 3;
|
|
||||||
|
|
||||||
public const uint MaximumBufferArgumentTableEntries = 31;
|
|
||||||
|
|
||||||
public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
class CounterEvent : ICounterEvent
|
|
||||||
{
|
|
||||||
public CounterEvent()
|
|
||||||
{
|
|
||||||
Invalid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Invalid { get; set; }
|
|
||||||
public bool ReserveForHostAccess()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Flush() { }
|
|
||||||
|
|
||||||
public void Dispose() { }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Metal.State;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class DepthStencilCache : StateCache<MTLDepthStencilState, DepthStencilUid, DepthStencilUid>
|
|
||||||
{
|
|
||||||
private readonly MTLDevice _device;
|
|
||||||
|
|
||||||
public DepthStencilCache(MTLDevice device)
|
|
||||||
{
|
|
||||||
_device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override DepthStencilUid GetHash(DepthStencilUid descriptor)
|
|
||||||
{
|
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor)
|
|
||||||
{
|
|
||||||
// Create descriptors
|
|
||||||
|
|
||||||
ref StencilUid frontUid = ref descriptor.FrontFace;
|
|
||||||
|
|
||||||
using MTLStencilDescriptor frontFaceStencil = new()
|
|
||||||
{
|
|
||||||
StencilFailureOperation = frontUid.StencilFailureOperation,
|
|
||||||
DepthFailureOperation = frontUid.DepthFailureOperation,
|
|
||||||
DepthStencilPassOperation = frontUid.DepthStencilPassOperation,
|
|
||||||
StencilCompareFunction = frontUid.StencilCompareFunction,
|
|
||||||
ReadMask = frontUid.ReadMask,
|
|
||||||
WriteMask = frontUid.WriteMask
|
|
||||||
};
|
|
||||||
|
|
||||||
ref StencilUid backUid = ref descriptor.BackFace;
|
|
||||||
|
|
||||||
using MTLStencilDescriptor backFaceStencil = new()
|
|
||||||
{
|
|
||||||
StencilFailureOperation = backUid.StencilFailureOperation,
|
|
||||||
DepthFailureOperation = backUid.DepthFailureOperation,
|
|
||||||
DepthStencilPassOperation = backUid.DepthStencilPassOperation,
|
|
||||||
StencilCompareFunction = backUid.StencilCompareFunction,
|
|
||||||
ReadMask = backUid.ReadMask,
|
|
||||||
WriteMask = backUid.WriteMask
|
|
||||||
};
|
|
||||||
|
|
||||||
MTLDepthStencilDescriptor mtlDescriptor = new()
|
|
||||||
{
|
|
||||||
DepthCompareFunction = descriptor.DepthCompareFunction,
|
|
||||||
DepthWriteEnabled = descriptor.DepthWriteEnabled
|
|
||||||
};
|
|
||||||
|
|
||||||
if (descriptor.StencilTestEnabled)
|
|
||||||
{
|
|
||||||
mtlDescriptor.BackFaceStencil = backFaceStencil;
|
|
||||||
mtlDescriptor.FrontFaceStencil = frontFaceStencil;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (mtlDescriptor)
|
|
||||||
{
|
|
||||||
return _device.NewDepthStencilState(mtlDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
readonly struct DisposableBuffer : IDisposable
|
|
||||||
{
|
|
||||||
public MTLBuffer Value { get; }
|
|
||||||
|
|
||||||
public DisposableBuffer(MTLBuffer buffer)
|
|
||||||
{
|
|
||||||
Value = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (Value != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Value.SetPurgeableState(MTLPurgeableState.Empty);
|
|
||||||
Value.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
readonly struct DisposableSampler : IDisposable
|
|
||||||
{
|
|
||||||
public MTLSamplerState Value { get; }
|
|
||||||
|
|
||||||
public DisposableSampler(MTLSamplerState sampler)
|
|
||||||
{
|
|
||||||
Value = sampler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Value.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user