mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-17 02:35:47 +00:00
Compare commits
22 Commits
Canary-1.3
...
update/dot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebc775aeb5 | ||
|
|
e24eb13e07 | ||
|
|
c7572b5d30 | ||
|
|
2ea829f17b | ||
|
|
9c00ffa4b1 | ||
|
|
84f26f7276 | ||
|
|
f2105d6040 | ||
|
|
1f3e4674b5 | ||
|
|
342c811aca | ||
|
|
83502494d9 | ||
|
|
64238e6ec3 | ||
|
|
36bd919c53 | ||
|
|
f1df537e76 | ||
|
|
f9e71a5908 | ||
|
|
f20291ddf2 | ||
|
|
cc80621a17 | ||
|
|
6a1dec9f91 | ||
|
|
274ec74856 | ||
|
|
ac98ade572 | ||
|
|
e23213d290 | ||
|
|
6467720c5c | ||
|
|
010eab44ba |
93
.github/workflows/canary.yml
vendored
93
.github/workflows/canary.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Canary CI
|
name: Canary release job
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -19,6 +19,7 @@ concurrency: release
|
|||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.3"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||||
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
|
#- { 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:
|
||||||
@@ -43,25 +44,11 @@ jobs:
|
|||||||
- name: Overwrite csc problem matcher
|
- name: Overwrite csc problem matcher
|
||||||
run: echo "::add-matcher::.github/csc.json"
|
run: echo "::add-matcher::.github/csc.json"
|
||||||
|
|
||||||
- name: Install 7zip
|
|
||||||
run: |
|
|
||||||
sudo apt install -y 7zip
|
|
||||||
|
|
||||||
- name: Install gli
|
|
||||||
run: |
|
|
||||||
mkdir -p $HOME/.bin
|
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
|
||||||
chmod +x gli
|
|
||||||
mv gli $HOME/.bin/
|
|
||||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -82,20 +69,33 @@ jobs:
|
|||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: contains(matrix.platform.name, 'win')
|
if: matrix.platform.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish
|
pushd publish
|
||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
|
gh release download -R GreemDev/GLI -O gli.exe -p 'GitLabCli-win_x64.exe'
|
||||||
|
|
||||||
|
./gli.exe --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip"
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
|
chmod +x gli
|
||||||
|
mv gli $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish
|
pushd publish
|
||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
@@ -103,11 +103,11 @@ jobs:
|
|||||||
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
||||||
PLATFORM_NAME="${{ matrix.platform.name }}"
|
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
@@ -140,7 +140,7 @@ jobs:
|
|||||||
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
|
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
@@ -159,10 +159,10 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GitLabCli
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.bin
|
mkdir -p $HOME/.bin
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
chmod +x gli
|
chmod +x gli
|
||||||
mv gli $HOME/.bin/
|
mv gli $HOME/.bin/
|
||||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
@@ -183,10 +183,9 @@ jobs:
|
|||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
run: |
|
run: |
|
||||||
@@ -201,7 +200,7 @@ jobs:
|
|||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
|
||||||
|
|
||||||
create_gitlab_release:
|
create_gitlab_release:
|
||||||
name: Create GitLab Release
|
name: Create GitLab Release
|
||||||
@@ -212,40 +211,36 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install gli
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.bin
|
mkdir -p $HOME/.bin
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
chmod +x gli
|
chmod +x gli
|
||||||
mv gli $HOME/.bin/
|
mv gli $HOME/.bin/
|
||||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Get version info
|
|
||||||
id: version_info
|
|
||||||
run: |
|
|
||||||
echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
|
|
||||||
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
|
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Create tag
|
- name: Create tag
|
||||||
run: |
|
run: |
|
||||||
gli create-tag -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Canary-${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }}
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateTag "Canary-${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}"
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
run: |
|
run: |
|
||||||
gli create-release-from-generic-package-files -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r main -t "Canary ${{ steps.version_info.outputs.build_version }}" -b "**Full Changelog:** [${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=CreateReleaseFromGenericPackageFiles "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|main|Canary ${{ steps.version_info.outputs.build_version }}|**Full Changelog:** [${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
|
||||||
|
|
||||||
- name: Send notification webhook
|
- name: Send notification webhook
|
||||||
run: |
|
run: |
|
||||||
gli send-update-message -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -t ${{ steps.version_info.outputs.build_version }} -c FF4500 -w ${{ secrets.CANARY_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|FF4500|${{ secrets.CANARY_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
|
||||||
|
|
||||||
- name: Notify update server of new builds
|
- name: Notify update server of new builds
|
||||||
run: |
|
run: |
|
||||||
gli refresh-version-cache -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Canary
|
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=canary' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'
|
||||||
|
|
||||||
- name: Advance to the next version
|
|
||||||
run: |
|
|
||||||
gli increment-version -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Canary
|
|
||||||
|
|||||||
224
.github/workflows/debug_release.yml
vendored
Normal file
224
.github/workflows/debug_release.yml
vendored
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
name: Release job (Debug)
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
|
||||||
|
concurrency: release
|
||||||
|
|
||||||
|
env:
|
||||||
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.3"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||||
|
RELEASE: 1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Release for ${{ matrix.platform.name }}
|
||||||
|
runs-on: ${{ matrix.platform.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- { 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-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Overwrite csc problem matcher
|
||||||
|
run: echo "::add-matcher::.github/csc.json"
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} + 10))" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/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_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create output dir
|
||||||
|
run: "mkdir release_output"
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: |
|
||||||
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
|
|
||||||
|
- name: Packing Windows builds
|
||||||
|
if: matrix.platform.os == 'windows-latest'
|
||||||
|
run: |
|
||||||
|
pushd publish
|
||||||
|
rm libarmeilleure-jitsupport.dylib
|
||||||
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
gh release download -R GreemDev/GLI -O gli.exe -p 'GitLabCli-win_x64.exe'
|
||||||
|
|
||||||
|
./gli.exe --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip"
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
|
chmod +x gli
|
||||||
|
mv gli $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Packing Linux builds
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
pushd publish
|
||||||
|
rm libarmeilleure-jitsupport.dylib
|
||||||
|
chmod +x Ryujinx.sh Ryujinx
|
||||||
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build AppImage (Linux)
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
||||||
|
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
|
|
||||||
|
sudo apt install -y zsync desktop-file-utils appstream
|
||||||
|
|
||||||
|
mkdir -p tools
|
||||||
|
export PATH="$PATH:$(readlink -f tools)"
|
||||||
|
|
||||||
|
# Setup appimagetool
|
||||||
|
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
|
chmod +x tools/appimagetool
|
||||||
|
chmod +x distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
|
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||||
|
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
|
||||||
|
ARCH_NAME=x64
|
||||||
|
export ARCH=x86_64
|
||||||
|
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
|
||||||
|
ARCH_NAME=arm64
|
||||||
|
export ARCH=aarch64
|
||||||
|
else
|
||||||
|
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
|
||||||
|
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
|
pushd publish_appimage
|
||||||
|
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
||||||
|
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
|
||||||
|
popd
|
||||||
|
|
||||||
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
|
||||||
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
macos_release:
|
||||||
|
name: Release MacOS universal
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 17
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
|
chmod +x gli
|
||||||
|
mv gli $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} + 10))" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/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_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Publish macOS Ryujinx
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
||||||
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
|
||||||
|
|
||||||
|
create_gitlab_release:
|
||||||
|
name: Create GitLab Release
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs:
|
||||||
|
- macos_release
|
||||||
|
- release
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} + 10))" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
|
chmod +x gli
|
||||||
|
mv gli $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
run: |
|
||||||
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateReleaseFromGenericPackageFiles "Ryubing|${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}|test|THIS IS NOT INTENDED FOR END USER USAGE"
|
||||||
115
.github/workflows/release.yml
vendored
115
.github/workflows/release.yml
vendored
@@ -1,18 +1,15 @@
|
|||||||
name: Stable CI
|
name: Release job
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs: {}
|
||||||
is_bugfix_release:
|
|
||||||
description: "Bug fix release: If checked, this will increment the third number for only Stable, and leave the Major version alone for both Stable and Canary."
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
concurrency: release
|
concurrency: release
|
||||||
|
|
||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.3"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
@@ -23,8 +20,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||||
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
|
#- { 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:
|
||||||
@@ -37,29 +34,11 @@ jobs:
|
|||||||
- name: Overwrite csc problem matcher
|
- name: Overwrite csc problem matcher
|
||||||
run: echo "::add-matcher::.github/csc.json"
|
run: echo "::add-matcher::.github/csc.json"
|
||||||
|
|
||||||
- name: Install 7zip
|
|
||||||
run: |
|
|
||||||
sudo apt install -y 7zip
|
|
||||||
|
|
||||||
- name: Install gli
|
|
||||||
run: |
|
|
||||||
mkdir -p $HOME/.bin
|
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
|
||||||
chmod +x gli
|
|
||||||
mv gli $HOME/.bin/
|
|
||||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
else
|
|
||||||
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -79,20 +58,33 @@ jobs:
|
|||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: contains(matrix.platform.name, 'win')
|
if: matrix.platform.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish
|
pushd publish
|
||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
|
gh release download -R GreemDev/GLI -O gli.exe -p 'GitLabCli-win_x64.exe'
|
||||||
|
|
||||||
|
./gli.exe --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip"
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
|
chmod +x gli
|
||||||
|
mv gli $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish
|
pushd publish
|
||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
@@ -100,13 +92,13 @@ jobs:
|
|||||||
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
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
||||||
PLATFORM_NAME="${{ matrix.platform.name }}"
|
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
@@ -139,7 +131,7 @@ jobs:
|
|||||||
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
@@ -158,10 +150,10 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GitLabCli
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.bin
|
mkdir -p $HOME/.bin
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
chmod +x gli
|
chmod +x gli
|
||||||
mv gli $HOME/.bin/
|
mv gli $HOME/.bin/
|
||||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
@@ -182,14 +174,9 @@ jobs:
|
|||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
else
|
|
||||||
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
run: |
|
run: |
|
||||||
@@ -202,8 +189,7 @@ jobs:
|
|||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
||||||
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
|
|
||||||
|
|
||||||
create_gitlab_release:
|
create_gitlab_release:
|
||||||
name: Create GitLab Release
|
name: Create GitLab Release
|
||||||
@@ -214,45 +200,32 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install gli
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install GitLabCli
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.bin
|
mkdir -p $HOME/.bin
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||||
chmod +x gli
|
chmod +x gli
|
||||||
mv gli $HOME/.bin/
|
mv gli $HOME/.bin/
|
||||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Get version info
|
|
||||||
id: version_info
|
|
||||||
run: |
|
|
||||||
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
|
|
||||||
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
|
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
|
||||||
echo "commit_message=$(git log -1 --pretty=%B)" >> $GITHUB_OUTPUT
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
run: |
|
run: |
|
||||||
gli create-release-from-generic-package-files -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }} -t "${{ steps.version_info.outputs.build_version }}" -b "msd:${{ steps.version_info.outputs.build_version }}"
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateReleaseFromGenericPackageFiles "Ryubing|${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}|${{ steps.version_info.outputs.build_version }}|msd:${{ steps.version_info.outputs.build_version }}"
|
||||||
|
|
||||||
- name: Send notification webhook
|
- name: Send notification webhook
|
||||||
run: |
|
run: |
|
||||||
gli send-update-message -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -t ${{ steps.version_info.outputs.build_version }} -c 32cd32 -w ${{ secrets.STABLE_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|32cd32|${{ secrets.STABLE_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
|
||||||
|
|
||||||
- name: Notify update server of new builds
|
- name: Notify update server of new builds
|
||||||
run: |
|
run: |
|
||||||
gli refresh-version-cache -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Stable
|
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=stable' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'
|
||||||
|
|
||||||
- name: Advance to the next version
|
|
||||||
run: |
|
|
||||||
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
|
|
||||||
gli advance-version -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}
|
|
||||||
else
|
|
||||||
gli increment-version -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Stable
|
|
||||||
fi
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,8 +18,6 @@ build/
|
|||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
AppDir/
|
AppDir/
|
||||||
publish_appimage/
|
publish_appimage/
|
||||||
publish_ava/
|
|
||||||
publish_tmp_ava/
|
|
||||||
|
|
||||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||||
!packages/*/build/
|
!packages/*/build/
|
||||||
@@ -100,7 +98,6 @@ DocProject/Help/html
|
|||||||
|
|
||||||
# Click-Once directory
|
# Click-Once directory
|
||||||
publish/
|
publish/
|
||||||
RyubingMaintainerTools/
|
|
||||||
|
|
||||||
# Publish Web Output
|
# Publish Web Output
|
||||||
*.Publish.xml
|
*.Publish.xml
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"Languages": {
|
|
||||||
"ar_SA": "اَلْعَرَبِيَّةُ",
|
|
||||||
"de_DE": "Deutsch",
|
|
||||||
"el_GR": "Ελληνικά",
|
|
||||||
"en_US": "English (US)",
|
|
||||||
"es_ES": "Español (ES)",
|
|
||||||
"fr_FR": "Français (FR)",
|
|
||||||
"he_IL": "עִברִית",
|
|
||||||
"it_IT": "Italiano",
|
|
||||||
"ja_JP": "日本語",
|
|
||||||
"ko_KR": "한국어",
|
|
||||||
"no_NO": "Norsk",
|
|
||||||
"pl_PL": "Polski",
|
|
||||||
"pt_BR": "Português (BR)",
|
|
||||||
"ru_RU": "Русский",
|
|
||||||
"sv_SE": "Svenska",
|
|
||||||
"th_TH": "ภาษาไทย",
|
|
||||||
"tr_TR": "Türkçe",
|
|
||||||
"uk_UA": "Українська",
|
|
||||||
"zh_CN": "简体中文",
|
|
||||||
"zh_TW": "繁體中文 (台灣)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
|
|
||||||
# Ryubing Locales
|
|
||||||
|
|
||||||
Ryubing Locales uses a custom format, which uses a file for defining the supported languages and a folder of json files for the locales themselves.
|
|
||||||
Each json file holds the locales for a specific part of the emulator, e.g. the Setup Wizard locales are in `SetupWizard.json`, and each locale entry in the file includes all the supported languages in the same place.
|
|
||||||
|
|
||||||
## Languages
|
|
||||||
in the `/assets/` folder you will find the `Languages.json` file, which defines all the languages supported by the emulator.
|
|
||||||
The file includes a table of the langauge codes and their langauge names.
|
|
||||||
|
|
||||||
#Example of the format for Languages.json
|
|
||||||
{
|
|
||||||
"Languages": {
|
|
||||||
"ar_SA": "اَلْعَرَبِيَّةُ",
|
|
||||||
"en_US": "English (US)",
|
|
||||||
...
|
|
||||||
"zh_TW": "繁體中文 (台灣)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
## Locales
|
|
||||||
in the `/assets/Locales/` folder you will find the json files, which define all the locales supported by the emulator.
|
|
||||||
Each json file holds locales for a specific part of the emulator in a large array of locale objects.
|
|
||||||
Each locale is made up an ID used for lookup and a list of the languages and their matching translations.
|
|
||||||
Any empty string or null value will automatically use the English translation instead in the emulator.
|
|
||||||
|
|
||||||
### Format
|
|
||||||
When adding a new locale, you just need to add the ID and the en_US language translation, then the validation system will add default values for the rest of languages automatically, when rebuilding the project.
|
|
||||||
If you want to signal that a translation is supposed to match the English translation, you just have to replace the empty string with `null`.
|
|
||||||
When you want to check what translations are missing for a language just search for `"<lang_code>": ""`, e.g: `"en_US": ""` (but with any other language, as English will never be missing translations).
|
|
||||||
|
|
||||||
### Legacy file (Root.json)
|
|
||||||
Currently all older locales are stored in `Root.json`, but they are slowly being moved into newer, more descriptive json files, to make the locale system more accessible.
|
|
||||||
Do **not** add new locales to `Root.json`.
|
|
||||||
If no json file exists for the specific part of the emulator you're working on, you should instead add a new json file for that part.
|
|
||||||
|
|
||||||
#Example of the format for Root.json
|
|
||||||
{
|
|
||||||
"Locales": [
|
|
||||||
{
|
|
||||||
"ID": "MenuBarActionsOpenMiiEditor",
|
|
||||||
"Translations": {
|
|
||||||
"ar_SA": "",
|
|
||||||
"en_US": "Mii Editor",
|
|
||||||
...
|
|
||||||
"zh_TW": "Mii 編輯器"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "KeyNumber9",
|
|
||||||
"Translations": {
|
|
||||||
"ar_SA": "٩",
|
|
||||||
"en_US": "9",
|
|
||||||
...
|
|
||||||
"zh_TW": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,5 +1,72 @@
|
|||||||
{
|
{
|
||||||
|
"Info": {
|
||||||
|
"Format1": "The Locale file uses a custom Unified format.",
|
||||||
|
"Format2": "The file starts with a list of all the supported languages.",
|
||||||
|
"Format3": "Each locale is made up an ID used for lookup and a list",
|
||||||
|
"Format4": "of the languages and their matching translations.",
|
||||||
|
"Format5": "When adding a new locale you just need to add the ID and",
|
||||||
|
"Format6": "the en_US language translation, then the validation system",
|
||||||
|
"Format7": "will add the rest of the languages automatically on rebuild.",
|
||||||
|
"Format8": "By default the languages will be added with an empty string.",
|
||||||
|
"Format9": "Any empty string or null value will automatically match the",
|
||||||
|
"Format10": "English translation.",
|
||||||
|
"Format11": "If you want to signal that a translation is supposed to",
|
||||||
|
"Format12": "match the English translation, you just have to replace the",
|
||||||
|
"Format13": "empty string with null.",
|
||||||
|
"Format14": "Translators who want to check what translations are missing",
|
||||||
|
"Format15": "for their language just need to search for:",
|
||||||
|
"Format16": "{'lang_code': ''} with double quotes instead of single",
|
||||||
|
"Format17": "e.g: {'en_US': ''} (but with any other language as English",
|
||||||
|
"Format18": "will never be missing translations)."
|
||||||
|
},
|
||||||
|
"Languages": [
|
||||||
|
"ar_SA",
|
||||||
|
"de_DE",
|
||||||
|
"el_GR",
|
||||||
|
"en_US",
|
||||||
|
"es_ES",
|
||||||
|
"fr_FR",
|
||||||
|
"he_IL",
|
||||||
|
"it_IT",
|
||||||
|
"ja_JP",
|
||||||
|
"ko_KR",
|
||||||
|
"no_NO",
|
||||||
|
"pl_PL",
|
||||||
|
"pt_BR",
|
||||||
|
"ru_RU",
|
||||||
|
"sv_SE",
|
||||||
|
"th_TH",
|
||||||
|
"tr_TR",
|
||||||
|
"uk_UA",
|
||||||
|
"zh_CN",
|
||||||
|
"zh_TW"
|
||||||
|
],
|
||||||
"Locales": [
|
"Locales": [
|
||||||
|
{
|
||||||
|
"ID": "Language",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "اَلْعَرَبِيَّةُ",
|
||||||
|
"de_DE": "Deutsch",
|
||||||
|
"el_GR": "Ελληνικά",
|
||||||
|
"en_US": "English (US)",
|
||||||
|
"es_ES": "Español (ES)",
|
||||||
|
"fr_FR": "Français (FR)",
|
||||||
|
"he_IL": "עִברִית",
|
||||||
|
"it_IT": "Italiano",
|
||||||
|
"ja_JP": "日本語",
|
||||||
|
"ko_KR": "한국어",
|
||||||
|
"no_NO": "Norsk",
|
||||||
|
"pl_PL": "Polski",
|
||||||
|
"pt_BR": "Português (BR)",
|
||||||
|
"ru_RU": "Русский",
|
||||||
|
"sv_SE": "Svenska",
|
||||||
|
"th_TH": "ภาษาไทย",
|
||||||
|
"tr_TR": "Türkçe",
|
||||||
|
"uk_UA": "Українська",
|
||||||
|
"zh_CN": "简体中文",
|
||||||
|
"zh_TW": "繁體中文 (台灣)"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarActionsOpenMiiEditor",
|
"ID": "MenuBarActionsOpenMiiEditor",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -158,7 +225,7 @@
|
|||||||
"el_GR": "Χωρίς Ελέγχους (γρηγορότερο, μη ασφαλές)",
|
"el_GR": "Χωρίς Ελέγχους (γρηγορότερο, μη ασφαλές)",
|
||||||
"en_US": "Host Unchecked (Fastest, Unsafe)",
|
"en_US": "Host Unchecked (Fastest, Unsafe)",
|
||||||
"es_ES": "Host Sin Verificación (Más rápido, Inseguro)",
|
"es_ES": "Host Sin Verificación (Más rápido, Inseguro)",
|
||||||
"fr_FR": "Hôte Non Vérifié (Plus Rapide, Non Sécurisé)",
|
"fr_FR": "Hôte Non Vérifié (plus rapide, non sécurisé)",
|
||||||
"he_IL": "מארח לא מבוקר (המהיר ביותר, לא בטוח)",
|
"he_IL": "מארח לא מבוקר (המהיר ביותר, לא בטוח)",
|
||||||
"it_IT": "Host senza verifica (più veloce, non sicura)",
|
"it_IT": "Host senza verifica (più veloce, non sicura)",
|
||||||
"ja_JP": "ホスト, チェックなし (最高速, 安全でない)",
|
"ja_JP": "ホスト, チェックなし (最高速, 安全でない)",
|
||||||
@@ -3283,7 +3350,7 @@
|
|||||||
"el_GR": "Ερώτημα",
|
"el_GR": "Ερώτημα",
|
||||||
"en_US": "Prompt",
|
"en_US": "Prompt",
|
||||||
"es_ES": "Al Inicio",
|
"es_ES": "Al Inicio",
|
||||||
"fr_FR": "Demander",
|
"fr_FR": "Demande",
|
||||||
"he_IL": "הודעה",
|
"he_IL": "הודעה",
|
||||||
"it_IT": "Domanda",
|
"it_IT": "Domanda",
|
||||||
"ja_JP": "プロンプト",
|
"ja_JP": "プロンプト",
|
||||||
@@ -4733,7 +4800,7 @@
|
|||||||
"el_GR": "Backend Ήχου:",
|
"el_GR": "Backend Ήχου:",
|
||||||
"en_US": "Audio Backend:",
|
"en_US": "Audio Backend:",
|
||||||
"es_ES": "Motor de Audio:",
|
"es_ES": "Motor de Audio:",
|
||||||
"fr_FR": "Moteur Audio :",
|
"fr_FR": "Bibliothèque Audio :",
|
||||||
"he_IL": "אחראי שמע:",
|
"he_IL": "אחראי שמע:",
|
||||||
"it_IT": "Backend audio:",
|
"it_IT": "Backend audio:",
|
||||||
"ja_JP": "音声バックエンド:",
|
"ja_JP": "音声バックエンド:",
|
||||||
@@ -5633,7 +5700,7 @@
|
|||||||
"el_GR": "Έκταση σε όλο το παράθυρο",
|
"el_GR": "Έκταση σε όλο το παράθυρο",
|
||||||
"en_US": "Stretch to Fit Window",
|
"en_US": "Stretch to Fit Window",
|
||||||
"es_ES": "Estirar a la Ventana",
|
"es_ES": "Estirar a la Ventana",
|
||||||
"fr_FR": "Adapter à la Taille de la Fenêtre",
|
"fr_FR": "Ajuster à la Taille de la Fenêtre",
|
||||||
"he_IL": "מתח לגודל חלון",
|
"he_IL": "מתח לגודל חלון",
|
||||||
"it_IT": "Adatta alla finestra",
|
"it_IT": "Adatta alla finestra",
|
||||||
"ja_JP": "ウインドウサイズに合わせる",
|
"ja_JP": "ウインドウサイズに合わせる",
|
||||||
@@ -5683,7 +5750,7 @@
|
|||||||
"el_GR": "Τοποθεσία Shaders Γραφικών:",
|
"el_GR": "Τοποθεσία Shaders Γραφικών:",
|
||||||
"en_US": "Graphics Shader Dump Path:",
|
"en_US": "Graphics Shader Dump Path:",
|
||||||
"es_ES": "Directorio de Volcado de Sombreadores:",
|
"es_ES": "Directorio de Volcado de Sombreadores:",
|
||||||
"fr_FR": "Chemin du dump des shaders graphiques :",
|
"fr_FR": "Chemin du Dossier de Copie des Shaders :",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Percorso di dump degli shader:",
|
"it_IT": "Percorso di dump degli shader:",
|
||||||
"ja_JP": "グラフィックス シェーダー ダンプパス:",
|
"ja_JP": "グラフィックス シェーダー ダンプパス:",
|
||||||
@@ -14933,7 +15000,7 @@
|
|||||||
"el_GR": "Πολυνηματική Επεξεργασία Γραφικών:",
|
"el_GR": "Πολυνηματική Επεξεργασία Γραφικών:",
|
||||||
"en_US": "Graphics Backend Multithreading:",
|
"en_US": "Graphics Backend Multithreading:",
|
||||||
"es_ES": "Multihilado del Motor Gráfico:",
|
"es_ES": "Multihilado del Motor Gráfico:",
|
||||||
"fr_FR": "Interface Graphique Multithread :",
|
"fr_FR": "Interface graphique multithread :",
|
||||||
"he_IL": "אחראי גרפיקה רב-תהליכי:",
|
"he_IL": "אחראי גרפיקה רב-תהליכי:",
|
||||||
"it_IT": "Multithreading del backend grafico:",
|
"it_IT": "Multithreading del backend grafico:",
|
||||||
"ja_JP": "グラフィックスバックエンドのマルチスレッド実行:",
|
"ja_JP": "グラフィックスバックエンドのマルチスレッド実行:",
|
||||||
@@ -16531,9 +16598,9 @@
|
|||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "Diese Option überspringt den Dialog 'Benutzerprofile verwalten' während des Spiels und verwendet ein voreingestelltes Profil.\n\nDie Profilumschaltung finden Sie unter 'Einstellungen' - 'Benutzerprofile verwalten'. Wählen Sie das gewünschte Profil aus, bevor Sie das Spiel laden.",
|
"de_DE": "Diese Option überspringt den Dialog 'Benutzerprofile verwalten' während des Spiels und verwendet ein voreingestelltes Profil.\n\nDie Profilumschaltung finden Sie unter 'Einstellungen' - 'Benutzerprofile verwalten'. Wählen Sie das gewünschte Profil aus, bevor Sie das Spiel laden.",
|
||||||
"el_GR": "Αυτή η επιλογή παρακάμπτει το παράθυρο διαλόγου 'Διαχειριστής Προφίλ Χρήστη' κατά τη διάρκεια του παιχνιδιού, χρησιμοποιώντας ένα προεπιλεγμένο προφίλ.\n\nΗ εναλλαγή προφίλ βρίσκεται στις 'Ρυθμίσεις' - 'Διαχειριστής Προφίλ Χρήστη'. Επιλέξτε το επιθυμητό προφίλ πριν φορτώσετε το παιχνίδι.",
|
"el_GR": "Αυτή η επιλογή παρακάμπτει το παράθυρο διαλόγου 'Διαχειριστής Προφίλ Χρήστη' κατά τη διάρκεια του παιχνιδιού, χρησιμοποιώντας ένα προεπιλεγμένο προφίλ.\n\nΗ εναλλαγή προφίλ βρίσκεται στις 'Ρυθμίσεις' - 'Διαχειριστής Προφίλ Χρήστη'. Επιλέξτε το επιθυμητό προφίλ πριν φορτώσετε το παιχνίδι.",
|
||||||
"en_US": "This option skips the 'Manage User Profiles' dialog during gameplay, using a pre-selected profile.\n\nProfile switching is found in 'Options' - 'User Profiles'. Select the desired profile before loading the game.",
|
"en_US": "This option skips the 'Manage User Profiles' dialog during gameplay, using a pre-selected profile.\n\nProfile switching is found in 'Settings' - 'Manager User Profiles'. Select the desired profile before loading the game.",
|
||||||
"es_ES": "Esta opción omite el diálogo de 'Gestionar perfiles de usuario' durante el juego, utilizando un perfil preseleccionado.\n\nEl cambio de perfil se encuentra en 'Opciones' - 'Perfiles de Usuario'. Seleccione el perfil deseado antes de cargar el juego.",
|
"es_ES": "Esta opción omite el diálogo de 'Gestionar perfiles de usuario' durante el juego, utilizando un perfil preseleccionado.\n\nEl cambio de perfil se encuentra en 'Configuración' - 'Gestionar perfiles de usuario'. Seleccione el perfil deseado antes de cargar el juego.",
|
||||||
"fr_FR": "Cette option permet de passer le dialogue 'Gérer les profils d'utilisateurs' pendant le jeu, en utilisant un profil pré-sélectionné.\n\nLa sélection du profil se trouve dans 'Options' - 'Profils d'Utilisateurs'. Sélectionnez le profil souhaité avant de lancer le jeu.",
|
"fr_FR": "Cette option permet d'éviter le dialogue du 'Gérer les profils d'utilisateurs' pendant le jeu, en utilisant un profil pré-sélectionné.\n\nLa sélection du profil se trouve dans 'Paramètres' - 'Gérer les profils d'utilisateurs'. Sélectionnez le profil souhaité avant de lancer le jeu.",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Questa opzione salta la finestra di dialogo 'Gestisci i profili utente' durante il gioco, utilizzando un profilo pre-selezionato.\n\nIl cambio del profilo si trova in 'Impostazioni' - 'Gestisci i profili utente'. Seleziona il profilo desiderato prima di caricare il gioco.",
|
"it_IT": "Questa opzione salta la finestra di dialogo 'Gestisci i profili utente' durante il gioco, utilizzando un profilo pre-selezionato.\n\nIl cambio del profilo si trova in 'Impostazioni' - 'Gestisci i profili utente'. Seleziona il profilo desiderato prima di caricare il gioco.",
|
||||||
"ja_JP": "このオプションは、ゲームプレイ中に「ユーザプロファイルを管理」ダイアログをスキップし、事前に選択されたプロファイルを使用します。\n\nプロファイルの切り替えは、「設定」-「ユーザプロファイルを管理」で見つけることができます。ゲームのロード前に目的のプロファイルをを選択してください。",
|
"ja_JP": "このオプションは、ゲームプレイ中に「ユーザプロファイルを管理」ダイアログをスキップし、事前に選択されたプロファイルを使用します。\n\nプロファイルの切り替えは、「設定」-「ユーザプロファイルを管理」で見つけることができます。ゲームのロード前に目的のプロファイルをを選択してください。",
|
||||||
@@ -22453,26 +22520,26 @@
|
|||||||
{
|
{
|
||||||
"ID": "MultiplayerModeTooltip",
|
"ID": "MultiplayerModeTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم ldn_mitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.",
|
"ar_SA": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم LdnMitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.",
|
||||||
"de_DE": "Ändert den LDN-Mehrspielermodus.\n\nldn_mitm ändert die lokale drahtlose/lokale Spielfunktionalität in Spielen so, dass sie wie ein LAN funktioniert und lokale, netzwerkgleiche Verbindungen mit anderen Ryujinx-Instanzen und gehackten Nintendo Switch-Konsolen ermöglicht, auf denen das ldn_mitm-Modul installiert ist.\n\nMultiplayer erfordert, dass alle Spieler die gleiche Spielversion verwenden (d.h. Super Smash Bros. Ultimate v13.0.1 kann sich nicht mit v13.0.0 verbinden).\n\nIm Zweifelsfall auf DISABLED lassen.",
|
"de_DE": "Ändert den LDN-Mehrspielermodus.\n\nLdnMitm ändert die lokale drahtlose/lokale Spielfunktionalität in Spielen so, dass sie wie ein LAN funktioniert und lokale, netzwerkgleiche Verbindungen mit anderen Ryujinx-Instanzen und gehackten Nintendo Switch-Konsolen ermöglicht, auf denen das ldn_mitm-Modul installiert ist.\n\nMultiplayer erfordert, dass alle Spieler die gleiche Spielversion verwenden (d.h. Super Smash Bros. Ultimate v13.0.1 kann sich nicht mit v13.0.0 verbinden).\n\nIm Zweifelsfall auf DISABLED lassen.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Change LDN multiplayer mode.\n\nldn_mitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.",
|
"en_US": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.",
|
||||||
"es_ES": "Cambiar modo LDN multijugador.\n\nldn_mitm modificará la funcionalidad local de juego inalámbrico para funcionar como si fuera LAN, permitiendo locales conexiones de la misma red con otras instancias de Ryujinx y consolas hackeadas de Nintendo Switch que tienen instalado el módulo ldn_mitm.\n\nMultijugador requiere que todos los jugadores estén en la misma versión del juego (por ejemplo, Super Smash Bros. Ultimate v13.0.1 no se puede conectar a v13.0.0).\n\nDejar DESACTIVADO si no está seguro.",
|
"es_ES": "Cambiar modo LDN multijugador.\n\nLdnMitm modificará la funcionalidad local de juego inalámbrico para funcionar como si fuera LAN, permitiendo locales conexiones de la misma red con otras instancias de Ryujinx y consolas hackeadas de Nintendo Switch que tienen instalado el módulo ldn_mitm.\n\nMultijugador requiere que todos los jugadores estén en la misma versión del juego (por ejemplo, Super Smash Bros. Ultimate v13.0.1 no se puede conectar a v13.0.0).\n\nDejar DESACTIVADO si no está seguro.",
|
||||||
"fr_FR": "Change le mode multijoueur LDN.\n\nldn_mitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.",
|
"fr_FR": "Change le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Cambia la modalità multigiocatore LDN.\n\nldn_mitm modificherà la funzionalità locale wireless/local play nei giochi per funzionare come se fosse in modalità LAN, consentendo connessioni locali sulla stessa rete con altre istanze di Ryujinx e console Nintendo Switch modificate che hanno il modulo ldn_mitm installato.\n\nLa modalità multigiocatore richiede che tutti i giocatori usino la stessa versione del gioco (es. Super Smash Bros. Ultimate v13.0.1 non può connettersi con la v13.0.0).\n\nNel dubbio, lascia l'opzione su Disabilitato.",
|
"it_IT": "Cambia la modalità multigiocatore LDN.\n\nLdnMitm modificherà la funzionalità locale wireless/local play nei giochi per funzionare come se fosse in modalità LAN, consentendo connessioni locali sulla stessa rete con altre istanze di Ryujinx e console Nintendo Switch modificate che hanno il modulo ldn_mitm installato.\n\nLa modalità multigiocatore richiede che tutti i giocatori usino la stessa versione del gioco (es. Super Smash Bros. Ultimate v13.0.1 non può connettersi con la v13.0.0).\n\nNel dubbio, lascia l'opzione su Disabilitato.",
|
||||||
"ja_JP": "LDNマルチプレイヤーモードを変更します.\n\nldn_mitmモジュールがインストールされた, 他のRyujinxインスタンスや,ハックされたNintendo Switchコンソールとのローカル/同一ネットワーク接続を可能にします.\n\nマルチプレイでは, すべてのプレイヤーが同じゲームバージョンである必要があります(例:Super Smash Bros. Ultimate v13.0.1はv13.0.0に接続できません).\n\n不明な場合は「無効」のままにしてください.",
|
"ja_JP": "LDNマルチプレイヤーモードを変更します.\n\nldn_mitmモジュールがインストールされた, 他のRyujinxインスタンスや,ハックされたNintendo Switchコンソールとのローカル/同一ネットワーク接続を可能にします.\n\nマルチプレイでは, すべてのプレイヤーが同じゲームバージョンである必要があります(例:Super Smash Bros. Ultimate v13.0.1はv13.0.0に接続できません).\n\n不明な場合は「無効」のままにしてください.",
|
||||||
"ko_KR": "LDN 멀티플레이어 모드를 변경합니다.\n\nldn_mitm은 게임의 로컬 무선/로컬 플레이 기능을 LAN처럼 작동하도록 수정하여 다른 Ryujinx 인스턴스나 ldn_mitm 모듈이 설치된 해킹된 Nintendo Switch 콘솔과 로컬, 동일 네트워크 연결이 가능합니다.\n\n멀티플레이어는 모든 플레이어가 동일한 게임 버전을 사용해야 합니다(예 : 슈퍼 스매시브라더스 얼티밋 v13.0.1은 v13.0.0에 연결할 수 없음).\n\n모르면 비활성화 상태로 두세요.",
|
"ko_KR": "LDN 멀티플레이어 모드를 변경합니다.\n\nLdnMitm은 게임의 로컬 무선/로컬 플레이 기능을 LAN처럼 작동하도록 수정하여 다른 Ryujinx 인스턴스나 ldn_mitm 모듈이 설치된 해킹된 Nintendo Switch 콘솔과 로컬, 동일 네트워크 연결이 가능합니다.\n\n멀티플레이어는 모든 플레이어가 동일한 게임 버전을 사용해야 합니다(예 : 슈퍼 스매시브라더스 얼티밋 v13.0.1은 v13.0.0에 연결할 수 없음).\n\n모르면 비활성화 상태로 두세요.",
|
||||||
"no_NO": "Endre LDN flerspillermodus.\n\nldn_mitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.",
|
"no_NO": "Endre LDN flerspillermodus.\n\nLdnMitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Alterar o modo multiplayer LDN.\n\nldn_mitm modificará a funcionalidade de jogo sem fio/local nos jogos para funcionar como se fosse LAN, permitindo conexões locais, na mesma rede, com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que possuem o módulo ldn_mitm instalado.\n\nO multiplayer exige que todos os jogadores estejam na mesma versão do jogo (ex.: Super Smash Bros. Ultimate v13.0.1 não consegue se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.",
|
"pt_BR": "Alterar o modo multiplayer LDN.\n\nLdnMitm modificará a funcionalidade de jogo sem fio/local nos jogos para funcionar como se fosse LAN, permitindo conexões locais, na mesma rede, com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que possuem o módulo ldn_mitm instalado.\n\nO multiplayer exige que todos os jogadores estejam na mesma versão do jogo (ex.: Super Smash Bros. Ultimate v13.0.1 não consegue se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.",
|
||||||
"ru_RU": "Меняет многопользовательский режим LDN.\n\nldn_mitm модифицирует функциональность локальной беспроводной игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить выключенным.",
|
"ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить выключенным.",
|
||||||
"sv_SE": "Ändra LDN-flerspelarläge\n\nldn_mitm kommer att ändra lokal funktionalitet för trådlös/lokalt spel att fungera som om det vore ett LAN, vilket ger stöd för anslutningar med local och same-network med andra Ryujinx-instanser och hackade Nintendo Switch-konsoller som har modulen ldn_mitm installerad.\n\nFlerspelare kräver att alla spelare har samma spelversion (t.ex. Super Smash Bros. Ultimate v13.0.1 kan inte ansluta till v13.0.0).\n\nLämna INAKTIVERAD om du är osäker.",
|
"sv_SE": "Ändra LDN-flerspelarläge\n\nLdnMitm kommer att ändra lokal funktionalitet för trådlös/lokalt spel att fungera som om det vore ett LAN, vilket ger stöd för anslutningar med local och same-network med andra Ryujinx-instanser och hackade Nintendo Switch-konsoller som har modulen ldn_mitm installerad.\n\nFlerspelare kräver att alla spelare har samma spelversion (t.ex. Super Smash Bros. Ultimate v13.0.1 kan inte ansluta till v13.0.0).\n\nLämna INAKTIVERAD om du är osäker.",
|
||||||
"th_TH": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nldn_mitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ",
|
"th_TH": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Змінити LDN мультиплеєру.\n\nldn_mitm змінить функціонал бездротової/локальної гри в іграх, щоб вони працювали так, ніби це LAN, що дозволяє локальні підключення в тій самій мережі з іншими екземплярами Ryujinx та хакнутими консолями Nintendo Switch, які мають встановлений модуль ldn_mitm.\n\nМультиплеєр вимагає, щоб усі гравці були на одній і тій же версії гри (наприклад Super Smash Bros. Ultimate v13.0.1 не зможе під'єднатися до v13.0.0).\n\nЗалиште на \"Вимкнено\", якщо не впевнені.",
|
"uk_UA": "Змінити LDN мультиплеєру.\n\nLdnMitm змінить функціонал бездротової/локальної гри в іграх, щоб вони працювали так, ніби це LAN, що дозволяє локальні підключення в тій самій мережі з іншими екземплярами Ryujinx та хакнутими консолями Nintendo Switch, які мають встановлений модуль ldn_mitm.\n\nМультиплеєр вимагає, щоб усі гравці були на одній і тій же версії гри (наприклад Super Smash Bros. Ultimate v13.0.1 не зможе під'єднатися до v13.0.0).\n\nЗалиште на \"Вимкнено\", якщо не впевнені.",
|
||||||
"zh_CN": "修改 LDN 多人联机游玩模式。\n\nldn_mitm 联机插件将修改游戏中的本地无线和本地游玩功能,使其表现得像局域网一样,允许和其他安装了 ldn_mitm 插件的 Ryujinx 模拟器和破解的任天堂 Switch 主机在同一网络下进行本地连接,实现多人联机游玩。\n\n多人联机游玩要求所有玩家必须运行相同的游戏版本(例如,游戏版本 v13.0.1 无法与 v13.0.0 联机)。\n\n如果不确定,请保持为“禁用”。",
|
"zh_CN": "修改 LDN 多人联机游玩模式。\n\nldn_mitm 联机插件将修改游戏中的本地无线和本地游玩功能,使其表现得像局域网一样,允许和其他安装了 ldn_mitm 插件的 Ryujinx 模拟器和破解的任天堂 Switch 主机在同一网络下进行本地连接,实现多人联机游玩。\n\n多人联机游玩要求所有玩家必须运行相同的游戏版本(例如,游戏版本 v13.0.1 无法与 v13.0.0 联机)。\n\n如果不确定,请保持为“禁用”。",
|
||||||
"zh_TW": "變更 LDN 多人遊戲模式。\n\nldn_mitm 將修改遊戲中的本機無線/本機遊戲功能,使其如同區域網路一樣執行,允許與其他安裝了 ldn_mitm 模組的 Ryujinx 實例和已破解的 Nintendo Switch 遊戲機進行本機同網路連線。\n\n多人遊戲要求所有玩家使用相同的遊戲版本 (例如,Super Smash Bros. Ultimate v13.0.1 無法連接 v13.0.0)。\n\n如果不確定,請保持 Disabled (停用) 狀態。"
|
"zh_TW": "變更 LDN 多人遊戲模式。\n\nLdnMitm 將修改遊戲中的本機無線/本機遊戲功能,使其如同區域網路一樣執行,允許與其他安裝了 ldn_mitm 模組的 Ryujinx 實例和已破解的 Nintendo Switch 遊戲機進行本機同網路連線。\n\n多人遊戲要求所有玩家使用相同的遊戲版本 (例如,Super Smash Bros. Ultimate v13.0.1 無法連接 v13.0.0)。\n\n如果不確定,請保持 Disabled (停用) 狀態。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -22733,7 +22800,7 @@
|
|||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Generates a new passphrase, which can be shared with other players.",
|
"en_US": "Generates a new passphrase, which can be shared with other players.",
|
||||||
"es_ES": "Genera una nueva frase de contraseña, que puede ser compartida con otros jugadores.",
|
"es_ES": "Genera una nueva frase de contraseña, que puede ser compartida con otros jugadores.",
|
||||||
"fr_FR": "Génère un nouveau mot de passe, qui peut être partagé avec les autres.",
|
"fr_FR": "Génére un nouveau mot de passe, qui peut être partagé avec les autres.",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Genera una nuova passphrase, che può essere condivisa con altri giocatori.",
|
"it_IT": "Genera una nuova passphrase, che può essere condivisa con altri giocatori.",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
Binary file not shown.
@@ -10,8 +10,6 @@
|
|||||||
<string>Ryujinx</string>
|
<string>Ryujinx</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>Ryujinx.icns</string>
|
<string>Ryujinx.icns</string>
|
||||||
<key>CFBundleIconName</key>
|
|
||||||
<string>Ryujinx</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@@ -42,7 +40,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.3</string>
|
<string>1.2</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
|||||||
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
|
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
|
||||||
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
|
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
|
||||||
cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh"
|
cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh"
|
||||||
cp Assets.car "$APP_BUNDLE_DIRECTORY/Contents/Resources/Assets.car"
|
|
||||||
cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||||
|
|
||||||
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
|
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
|
||||||
|
|||||||
124
distribution/macos/create_macos_build_headless.sh
Executable file
124
distribution/macos/create_macos_build_headless.sh
Executable file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$#" -lt 8 ]; then
|
||||||
|
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <CANARY>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$1"
|
||||||
|
mkdir -p "$2"
|
||||||
|
mkdir -p "$3"
|
||||||
|
|
||||||
|
BASE_DIR=$(readlink -f "$1")
|
||||||
|
TEMP_DIRECTORY=$(readlink -f "$2")
|
||||||
|
OUTPUT_DIRECTORY=$(readlink -f "$3")
|
||||||
|
ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
|
||||||
|
VERSION=$5
|
||||||
|
SOURCE_REVISION_ID=$6
|
||||||
|
CONFIGURATION=$7
|
||||||
|
CANARY=$8
|
||||||
|
|
||||||
|
if [[ "$(uname)" == "Darwin" ]]; then
|
||||||
|
echo "Clearing xattr on all dot undercsore files"
|
||||||
|
find "$BASE_DIR" -type f -name "._*" -exec sh -c '
|
||||||
|
for f; do
|
||||||
|
dir=$(dirname "$f")
|
||||||
|
base=$(basename "$f")
|
||||||
|
orig="$dir/${base#._}"
|
||||||
|
[ -f "$orig" ] && xattr -c "$orig" || true
|
||||||
|
done
|
||||||
|
' sh {} +
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CANARY" == "1" ]; then
|
||||||
|
RELEASE_TAR_FILE_NAME=nogui-ryujinx-canary-$VERSION-macos_universal.tar
|
||||||
|
elif [ "$VERSION" == "1.1.0" ]; then
|
||||||
|
RELEASE_TAR_FILE_NAME=nogui-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar
|
||||||
|
else
|
||||||
|
RELEASE_TAR_FILE_NAME=nogui-ryujinx-$VERSION-macos_universal.tar
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64"
|
||||||
|
X64_OUTPUT="$TEMP_DIRECTORY/publish_x64"
|
||||||
|
UNIVERSAL_OUTPUT="$OUTPUT_DIRECTORY/publish"
|
||||||
|
EXECUTABLE_SUB_PATH=Ryujinx.Headless.SDL3
|
||||||
|
|
||||||
|
rm -rf "$TEMP_DIRECTORY"
|
||||||
|
mkdir -p "$TEMP_DIRECTORY"
|
||||||
|
|
||||||
|
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
||||||
|
|
||||||
|
dotnet restore
|
||||||
|
dotnet build -c "$CONFIGURATION" src/Ryujinx.Headless.SDL3
|
||||||
|
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL3
|
||||||
|
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL3
|
||||||
|
|
||||||
|
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||||
|
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||||
|
|
||||||
|
# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant
|
||||||
|
# TODO: remove this once done
|
||||||
|
rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib"
|
||||||
|
|
||||||
|
rm -rf "$OUTPUT_DIRECTORY"
|
||||||
|
mkdir -p "$OUTPUT_DIRECTORY"
|
||||||
|
|
||||||
|
# Let's copy one of the two different outputs and remove the executable
|
||||||
|
cp -R "$ARM64_OUTPUT/" "$UNIVERSAL_OUTPUT"
|
||||||
|
rm "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH"
|
||||||
|
|
||||||
|
# Make its libraries universal
|
||||||
|
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTPUT" "$X64_OUTPUT" "$UNIVERSAL_OUTPUT" "**/*.dylib"
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
|
then
|
||||||
|
if ! [ -x "$(command -v llvm-lipo-17)" ];
|
||||||
|
then
|
||||||
|
LIPO=llvm-lipo
|
||||||
|
else
|
||||||
|
LIPO=llvm-lipo-17
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
LIPO=lipo
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make the executable universal
|
||||||
|
$LIPO "$ARM64_OUTPUT/$EXECUTABLE_SUB_PATH" "$X64_OUTPUT/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH" -create
|
||||||
|
|
||||||
|
# Now sign it
|
||||||
|
if ! [ -x "$(command -v codesign)" ];
|
||||||
|
then
|
||||||
|
if ! [ -x "$(command -v rcodesign)" ];
|
||||||
|
then
|
||||||
|
echo "Cannot find rcodesign on your system, please install rcodesign."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
|
||||||
|
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
|
||||||
|
echo "Using rcodesign for ad-hoc signing"
|
||||||
|
for FILE in "$UNIVERSAL_OUTPUT"/*; do
|
||||||
|
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
|
||||||
|
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$FILE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "Using codesign for ad-hoc signing"
|
||||||
|
for FILE in "$UNIVERSAL_OUTPUT"/*; do
|
||||||
|
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
|
||||||
|
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$FILE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating archive"
|
||||||
|
pushd "$OUTPUT_DIRECTORY"
|
||||||
|
tar --exclude "publish/Ryujinx.Headless.SDL3" -cvf "$RELEASE_TAR_FILE_NAME" publish 1> /dev/null
|
||||||
|
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "publish/Ryujinx.Headless.SDL3" "publish/Ryujinx.Headless.SDL3"
|
||||||
|
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||||
|
rm "$RELEASE_TAR_FILE_NAME"
|
||||||
|
popd
|
||||||
|
|
||||||
|
echo "Done"
|
||||||
@@ -2275,12 +2275,12 @@
|
|||||||
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
|
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
|
||||||
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
|
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
|
||||||
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
|
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
|
||||||
0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00
|
|
||||||
01005D100807A000,"Pokémon™ Quest",,playable,2022-02-22 16:12:32
|
01005D100807A000,"Pokémon™ Quest",,playable,2022-02-22 16:12:32
|
||||||
0100A3D008C5C000,"Pokémon™ Scarlet",gpu;nvdec;ldn-works;amd-vendor-bug,ingame,2023-12-14 13:18:29
|
0100A3D008C5C000,"Pokémon™ Scarlet",gpu;nvdec;ldn-works;amd-vendor-bug,ingame,2023-12-14 13:18:29
|
||||||
01008F6008C5E000,"Pokémon™ Violet",gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug,ingame,2024-07-30 02:51:48
|
01008F6008C5E000,"Pokémon™ Violet",gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug,ingame,2024-07-30 02:51:48
|
||||||
0100187003A36000,"Pokémon™: Let’s Go, Eevee!",crash;nvdec;online-broken;ldn-broken,ingame,2024-06-01 15:03:04
|
0100187003A36000,"Pokémon™: Let’s Go, Eevee!",crash;nvdec;online-broken;ldn-broken,ingame,2024-06-01 15:03:04
|
||||||
010003F003A34000,"Pokémon™: Let’s Go, Pikachu!",crash;nvdec;online-broken;ldn-broken,ingame,2024-03-15 07:55:41
|
010003F003A34000,"Pokémon™: Let’s Go, Pikachu!",crash;nvdec;online-broken;ldn-broken,ingame,2024-03-15 07:55:41
|
||||||
|
0100F43008C44000,"Pokémon Legends: Z-A",gpu;crash;ldn-broken,ingame,2025-10-16 19:13:00
|
||||||
0100B3F000BE2000,"Pokkén Tournament™ DX",nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug,playable,2024-07-18 23:11:08
|
0100B3F000BE2000,"Pokkén Tournament™ DX",nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug,playable,2024-07-18 23:11:08
|
||||||
010030D005AE6000,"Pokkén Tournament™ DX Demo",demo;opengl-backend-bug,playable,2022-08-10 12:03:19
|
010030D005AE6000,"Pokkén Tournament™ DX Demo",demo;opengl-backend-bug,playable,2022-08-10 12:03:19
|
||||||
0100A3500B4EC000,"Polandball: Can Into Space",,playable,2020-06-25 15:13:26
|
0100A3500B4EC000,"Polandball: Can Into Space",,playable,2020-06-25 15:13:26
|
||||||
|
|||||||
|
@@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
context.Return(Const(op.Address));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Svc(ArmEmitterContext context)
|
public static void Svc(ArmEmitterContext context)
|
||||||
@@ -49,7 +49,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
context.Return(Const(op.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitReturn(context, Const(context.CurrOp.Address));
|
context.Return(Const(context.CurrOp.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||||
|
|
||||||
EmitReturn(context, GetIntOrZR(context, op.Rn));
|
context.Return(GetIntOrZR(context, op.Rn));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
static class InstEmitFlowHelper
|
static class InstEmitFlowHelper
|
||||||
{
|
{
|
||||||
// How many calls we can have in our call stack before we give up and return to the dispatcher.
|
|
||||||
// This prevents stack overflows caused by deep recursive calls.
|
|
||||||
private const int MaxCallDepth = 200;
|
|
||||||
|
|
||||||
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
||||||
{
|
{
|
||||||
if (cond != Condition.Al)
|
if (cond != Condition.Al)
|
||||||
@@ -186,7 +182,12 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
if (isReturn || context.IsSingleStep)
|
if (isReturn || context.IsSingleStep)
|
||||||
{
|
{
|
||||||
EmitReturn(context, target);
|
if (target.Type == OperandType.I32)
|
||||||
|
{
|
||||||
|
target = context.ZeroExtend32(OperandType.I64, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Return(target);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -194,19 +195,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitReturn(ArmEmitterContext context, Operand target)
|
|
||||||
{
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
DecreaseCallDepth(context, nativeContext);
|
|
||||||
|
|
||||||
if (target.Type == OperandType.I32)
|
|
||||||
{
|
|
||||||
target = context.ZeroExtend32(OperandType.I64, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Return(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
||||||
{
|
{
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
@@ -269,8 +257,6 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
if (isJump)
|
if (isJump)
|
||||||
{
|
{
|
||||||
DecreaseCallDepth(context, nativeContext);
|
|
||||||
|
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
context.Tailcall(hostAddress, nativeContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -292,42 +278,8 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
||||||
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
||||||
|
|
||||||
DecreaseCallDepth(context, nativeContext);
|
|
||||||
|
|
||||||
context.Return(returnAddress);
|
context.Return(returnAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitCallDepthCheckAndIncrement(EmitterContext context, Operand guestAddress)
|
|
||||||
{
|
|
||||||
if (!Optimizations.EnableDeepCallRecursionProtection)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
|
||||||
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
|
|
||||||
Operand lblDoCall = Label();
|
|
||||||
|
|
||||||
context.BranchIf(lblDoCall, currentCallDepth, Const(MaxCallDepth), Comparison.LessUI);
|
|
||||||
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
|
|
||||||
context.Return(guestAddress);
|
|
||||||
|
|
||||||
context.MarkLabel(lblDoCall);
|
|
||||||
context.Store(callDepthAddr, context.Add(currentCallDepth, Const(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DecreaseCallDepth(EmitterContext context, Operand nativeContext)
|
|
||||||
{
|
|
||||||
if (!Optimizations.EnableDeepCallRecursionProtection)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
|
||||||
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
|
|
||||||
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ namespace ARMeilleure
|
|||||||
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
||||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
||||||
public static bool EnableDebugging { get; set; } = false;
|
public static bool EnableDebugging { get; set; } = false;
|
||||||
public static bool EnableDeepCallRecursionProtection { get; set; } = true;
|
|
||||||
|
|
||||||
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
||||||
public static bool UseArm64AesIfAvailable { get; set; } = true;
|
public static bool UseArm64AesIfAvailable { get; set; } = true;
|
||||||
|
|||||||
@@ -134,11 +134,6 @@ namespace ARMeilleure.State
|
|||||||
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
||||||
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
||||||
|
|
||||||
internal void ResetCallDepth()
|
|
||||||
{
|
|
||||||
_nativeContext.ResetCallDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void CheckInterrupt()
|
internal void CheckInterrupt()
|
||||||
{
|
{
|
||||||
if (Interrupted)
|
if (Interrupted)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace ARMeilleure.State
|
|||||||
public ulong ExclusiveValueHigh;
|
public ulong ExclusiveValueHigh;
|
||||||
public int Running;
|
public int Running;
|
||||||
public long Tpidr2El0;
|
public long Tpidr2El0;
|
||||||
public int CallDepth;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Precise PC value used for debugging.
|
/// Precise PC value used for debugging.
|
||||||
@@ -200,8 +199,6 @@ namespace ARMeilleure.State
|
|||||||
public bool GetRunning() => GetStorage().Running != 0;
|
public bool GetRunning() => GetStorage().Running != 0;
|
||||||
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
||||||
|
|
||||||
public void ResetCallDepth() => GetStorage().CallDepth = 0;
|
|
||||||
|
|
||||||
public unsafe static int GetRegisterOffset(Register reg)
|
public unsafe static int GetRegisterOffset(Register reg)
|
||||||
{
|
{
|
||||||
if (reg.Type == RegisterType.Integer)
|
if (reg.Type == RegisterType.Integer)
|
||||||
@@ -287,11 +284,6 @@ namespace ARMeilleure.State
|
|||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetCallDepthOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
||||||
{
|
{
|
||||||
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
||||||
|
|||||||
@@ -361,7 +361,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||||
|
|
||||||
tmp?.Parent = ParentOf(replacementNode);
|
if (tmp != null)
|
||||||
|
{
|
||||||
|
tmp.Parent = ParentOf(replacementNode);
|
||||||
|
}
|
||||||
|
|
||||||
if (ParentOf(replacementNode) == null)
|
if (ParentOf(replacementNode) == null)
|
||||||
{
|
{
|
||||||
@@ -579,7 +582,10 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
IntervalTreeNode<TK, TV> right = RightOf(node);
|
IntervalTreeNode<TK, TV> right = RightOf(node);
|
||||||
node.Right = LeftOf(right);
|
node.Right = LeftOf(right);
|
||||||
node.Right?.Parent = node;
|
if (node.Right != null)
|
||||||
|
{
|
||||||
|
node.Right.Parent = node;
|
||||||
|
}
|
||||||
|
|
||||||
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
||||||
right.Parent = nodeParent;
|
right.Parent = nodeParent;
|
||||||
@@ -609,7 +615,10 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
IntervalTreeNode<TK, TV> left = LeftOf(node);
|
IntervalTreeNode<TK, TV> left = LeftOf(node);
|
||||||
node.Left = RightOf(left);
|
node.Left = RightOf(left);
|
||||||
node.Left?.Parent = node;
|
if (node.Left != null)
|
||||||
|
{
|
||||||
|
node.Left.Parent = node;
|
||||||
|
}
|
||||||
|
|
||||||
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
||||||
left.Parent = nodeParent;
|
left.Parent = nodeParent;
|
||||||
@@ -658,7 +667,10 @@ namespace ARMeilleure.Translation
|
|||||||
/// <param name="color">Color (Boolean)</param>
|
/// <param name="color">Color (Boolean)</param>
|
||||||
private static void SetColor(IntervalTreeNode<TK, TV> node, bool color)
|
private static void SetColor(IntervalTreeNode<TK, TV> node, bool color)
|
||||||
{
|
{
|
||||||
node?.Color = color;
|
if (node != null)
|
||||||
|
{
|
||||||
|
node.Color = color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 7010; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7009; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
|||||||
@@ -186,7 +186,6 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Statistics.StartTimer();
|
Statistics.StartTimer();
|
||||||
|
|
||||||
context.ResetCallDepth();
|
|
||||||
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
Statistics.StopTimer(address);
|
||||||
@@ -261,7 +260,6 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Logger.StartPass(PassName.Translation);
|
Logger.StartPass(PassName.Translation);
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCallDepthCheckAndIncrement(context, Const(address));
|
|
||||||
EmitSynchronization(context);
|
EmitSynchronization(context);
|
||||||
|
|
||||||
if (blocks[0].Address != address)
|
if (blocks[0].Address != address)
|
||||||
|
|||||||
@@ -262,18 +262,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
||||||
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
||||||
Operand callDepthAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
|
||||||
|
|
||||||
EmitSyncFpContext(context, nativeContext, true);
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
|
||||||
context.MarkLabel(beginLbl);
|
context.MarkLabel(beginLbl);
|
||||||
|
|
||||||
if (Optimizations.EnableDeepCallRecursionProtection)
|
|
||||||
{
|
|
||||||
// Reset the call depth counter, since this is our first guest function call.
|
|
||||||
context.Store(callDepthAddress, Const(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Store(dispatchAddress, guestAddress);
|
context.Store(dispatchAddress, guestAddress);
|
||||||
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
||||||
context.BranchIfFalse(endLbl, guestAddress);
|
context.BranchIfFalse(endLbl, guestAddress);
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
|
short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
|
||||||
Array.Fill(outputBuffer, (short)0, 0, (int)inputCount * SampleCount);
|
|
||||||
|
|
||||||
for (int i = 0; i < bufferCount; i++)
|
for (int i = 0; i < bufferCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Array6<byte> Output;
|
public Array6<byte> Output;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reserved/unused.
|
|
||||||
/// </summary>
|
|
||||||
private readonly uint _padding;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Biquad filter numerator (b0, b1, b2).
|
/// Biquad filter numerator (b0, b1, b2).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ namespace Ryujinx.BuildValidationTasks
|
|||||||
{
|
{
|
||||||
static readonly JsonSerializerOptions _jsonOptions = new()
|
static readonly JsonSerializerOptions _jsonOptions = new()
|
||||||
{
|
{
|
||||||
WriteIndented = true, NewLine = "\n", Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
WriteIndented = true,
|
||||||
|
NewLine = "\n",
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
};
|
};
|
||||||
|
|
||||||
public LocalesValidationTask() { }
|
public LocalesValidationTask() { }
|
||||||
@@ -20,116 +22,77 @@ namespace Ryujinx.BuildValidationTasks
|
|||||||
{
|
{
|
||||||
Console.WriteLine("Running Locale Validation Task...");
|
Console.WriteLine("Running Locale Validation Task...");
|
||||||
|
|
||||||
bool encounteredIssue = false;
|
string path = projectPath + "assets/locales.json";
|
||||||
string langPath = projectPath + "assets/Languages.json";
|
|
||||||
string data;
|
string data;
|
||||||
|
|
||||||
using (StreamReader sr = new(langPath))
|
using (StreamReader sr = new(path))
|
||||||
{
|
{
|
||||||
data = sr.ReadToEnd();
|
data = sr.ReadToEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isGitRunner && data.Contains("\r\n"))
|
LocalesJson json;
|
||||||
throw new FormatException("Languages.json is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
|
|
||||||
|
|
||||||
LanguagesJson langJson;
|
if (isGitRunner && data.Contains("\r\n"))
|
||||||
|
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
langJson = JsonSerializer.Deserialize<LanguagesJson>(data);
|
json = JsonSerializer.Deserialize<LocalesJson>(data);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (JsonException e)
|
catch (JsonException e)
|
||||||
{
|
{
|
||||||
throw new JsonException(e.Message); //shorter and easier stacktrace
|
throw new JsonException(e.Message); //shorter and easier stacktrace
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((string code, string lang) in langJson.Languages)
|
bool encounteredIssue = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < json.Locales.Count; i++)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(lang))
|
LocalesEntry locale = json.Locales[i];
|
||||||
|
|
||||||
|
foreach (string langCode in json.Languages.Where(lang => !locale.Translations.ContainsKey(lang)))
|
||||||
{
|
{
|
||||||
throw new JsonException($"{code} language name missing!");
|
encounteredIssue = true;
|
||||||
|
|
||||||
|
if (!isGitRunner)
|
||||||
|
{
|
||||||
|
locale.Translations.Add(langCode, string.Empty);
|
||||||
|
Console.WriteLine($"Added '{langCode}' to Locale '{locale.ID}'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Missing '{langCode}' in Locale '{locale.ID}'!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (string langCode in json.Languages.Where(lang => locale.Translations.ContainsKey(lang) && lang != "en_US" && locale.Translations[lang] == locale.Translations["en_US"]))
|
||||||
|
{
|
||||||
|
encounteredIssue = true;
|
||||||
|
|
||||||
|
if (!isGitRunner)
|
||||||
|
{
|
||||||
|
locale.Translations[langCode] = string.Empty;
|
||||||
|
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
json.Locales[i] = locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
string folderPath = projectPath + "assets/Locales/";
|
if (isGitRunner && encounteredIssue)
|
||||||
|
throw new JsonException("1 or more locales are invalid! Rebuild locally to fix...");
|
||||||
|
|
||||||
string[] paths = Directory.GetFiles(folderPath, "*.json", SearchOption.AllDirectories);
|
string jsonString = JsonSerializer.Serialize(json, _jsonOptions);
|
||||||
|
|
||||||
foreach (string path in paths)
|
using (StreamWriter sw = new(path))
|
||||||
{
|
{
|
||||||
using (StreamReader sr = new(path))
|
sw.Write(jsonString);
|
||||||
{
|
|
||||||
data = sr.ReadToEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isGitRunner && data.Contains("\r\n"))
|
|
||||||
throw new FormatException($"{Path.GetFileName(path)} is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
|
|
||||||
|
|
||||||
LocalesJson json;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
json = JsonSerializer.Deserialize<LocalesJson>(data);
|
|
||||||
}
|
|
||||||
catch (JsonException e)
|
|
||||||
{
|
|
||||||
throw new JsonException(e.Message); //shorter and easier stacktrace
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < json.Locales.Count; i++)
|
|
||||||
{
|
|
||||||
LocalesEntry locale = json.Locales[i];
|
|
||||||
|
|
||||||
foreach (string langCode in
|
|
||||||
langJson.Languages.Keys.Where(lang => !locale.Translations.ContainsKey(lang)))
|
|
||||||
{
|
|
||||||
encounteredIssue = true;
|
|
||||||
|
|
||||||
if (!isGitRunner)
|
|
||||||
{
|
|
||||||
locale.Translations.Add(langCode, string.Empty);
|
|
||||||
Console.WriteLine($"Added '{langCode}' to Locale '{locale.ID}'");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Missing '{langCode}' in Locale '{locale.ID}'!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string langCode in langJson.Languages.Keys.Where(lang =>
|
|
||||||
locale.Translations.ContainsKey(lang) && lang != "en_US" &&
|
|
||||||
locale.Translations[lang] == locale.Translations["en_US"]))
|
|
||||||
{
|
|
||||||
encounteredIssue = true;
|
|
||||||
|
|
||||||
if (!isGitRunner)
|
|
||||||
{
|
|
||||||
locale.Translations[langCode] = string.Empty;
|
|
||||||
Console.WriteLine(
|
|
||||||
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine(
|
|
||||||
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
locale.Translations = locale.Translations.OrderBy(pair => pair.Key)
|
|
||||||
.ToDictionary(pair => pair.Key, pair => pair.Value);
|
|
||||||
json.Locales[i] = locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isGitRunner && encounteredIssue)
|
|
||||||
throw new JsonException("1 or more locales are invalid! Rebuild locally to fix...");
|
|
||||||
|
|
||||||
string jsonString = JsonSerializer.Serialize(json, _jsonOptions);
|
|
||||||
|
|
||||||
using (StreamWriter sw = new(path))
|
|
||||||
{
|
|
||||||
sw.Write(jsonString);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Finished Locale Validation Task!");
|
Console.WriteLine("Finished Locale Validation Task!");
|
||||||
@@ -137,13 +100,10 @@ namespace Ryujinx.BuildValidationTasks
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LanguagesJson
|
|
||||||
{
|
|
||||||
public Dictionary<string, string> Languages { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LocalesJson
|
struct LocalesJson
|
||||||
{
|
{
|
||||||
|
public Dictionary<string, string> Info { get; set; }
|
||||||
|
public List<string> Languages { get; set; }
|
||||||
public List<LocalesEntry> Locales { get; set; }
|
public List<LocalesEntry> Locales { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -386,7 +386,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
|
|
||||||
IntervalTreeNode<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
IntervalTreeNode<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||||
|
|
||||||
tmp?.Parent = ParentOf(replacementNode);
|
if (tmp != null)
|
||||||
|
{
|
||||||
|
tmp.Parent = ParentOf(replacementNode);
|
||||||
|
}
|
||||||
|
|
||||||
if (ParentOf(replacementNode) == null)
|
if (ParentOf(replacementNode) == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -235,7 +235,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
parent = ParentOf(element);
|
parent = ParentOf(element);
|
||||||
color = ColorOf(element);
|
color = ColorOf(element);
|
||||||
|
|
||||||
child?.Parent = parent;
|
if (child != null)
|
||||||
|
{
|
||||||
|
child.Parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
@@ -255,7 +258,8 @@ namespace Ryujinx.Common.Collections
|
|||||||
element.Right = old.Right;
|
element.Right = old.Right;
|
||||||
element.Parent = old.Parent;
|
element.Parent = old.Parent;
|
||||||
element.Predecessor = old.Predecessor;
|
element.Predecessor = old.Predecessor;
|
||||||
element.Predecessor?.Successor = element;
|
if (element.Predecessor != null)
|
||||||
|
element.Predecessor.Successor = element;
|
||||||
|
|
||||||
if (ParentOf(old) == null)
|
if (ParentOf(old) == null)
|
||||||
{
|
{
|
||||||
@@ -288,7 +292,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
parent = ParentOf(nodeToDelete);
|
parent = ParentOf(nodeToDelete);
|
||||||
color = ColorOf(nodeToDelete);
|
color = ColorOf(nodeToDelete);
|
||||||
|
|
||||||
child?.Parent = parent;
|
if (child != null)
|
||||||
|
{
|
||||||
|
child.Parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
@@ -308,8 +315,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
RestoreBalanceAfterRemoval(child);
|
RestoreBalanceAfterRemoval(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
old.Successor?.Predecessor = old.Predecessor;
|
if (old.Successor != null)
|
||||||
old.Predecessor?.Successor = old.Successor;
|
old.Successor.Predecessor = old.Predecessor;
|
||||||
|
if (old.Predecessor != null)
|
||||||
|
old.Predecessor.Successor = old.Successor;
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,7 +250,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
{
|
{
|
||||||
T right = RightOf(node);
|
T right = RightOf(node);
|
||||||
node.Right = LeftOf(right);
|
node.Right = LeftOf(right);
|
||||||
node.Right?.Parent = node;
|
if (node.Right != null)
|
||||||
|
{
|
||||||
|
node.Right.Parent = node;
|
||||||
|
}
|
||||||
|
|
||||||
T nodeParent = ParentOf(node);
|
T nodeParent = ParentOf(node);
|
||||||
right.Parent = nodeParent;
|
right.Parent = nodeParent;
|
||||||
@@ -278,7 +281,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
{
|
{
|
||||||
T left = LeftOf(node);
|
T left = LeftOf(node);
|
||||||
node.Left = RightOf(left);
|
node.Left = RightOf(left);
|
||||||
node.Left?.Parent = node;
|
if (node.Left != null)
|
||||||
|
{
|
||||||
|
node.Left.Parent = node;
|
||||||
|
}
|
||||||
|
|
||||||
T nodeParent = ParentOf(node);
|
T nodeParent = ParentOf(node);
|
||||||
left.Parent = nodeParent;
|
left.Parent = nodeParent;
|
||||||
@@ -323,7 +329,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
/// <param name="color">Color (Boolean)</param>
|
/// <param name="color">Color (Boolean)</param>
|
||||||
protected static void SetColor(T node, bool color)
|
protected static void SetColor(T node, bool color)
|
||||||
{
|
{
|
||||||
node?.Color = color;
|
if (node != null)
|
||||||
|
{
|
||||||
|
node.Color = color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -328,7 +328,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
|
|
||||||
Node<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
Node<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||||
|
|
||||||
tmp?.Parent = ParentOf(replacementNode);
|
if (tmp != null)
|
||||||
|
{
|
||||||
|
tmp.Parent = ParentOf(replacementNode);
|
||||||
|
}
|
||||||
|
|
||||||
if (ParentOf(replacementNode) == null)
|
if (ParentOf(replacementNode) == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<AntiAliasing>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
|
||||||
public enum AntiAliasing
|
public enum AntiAliasing
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<AspectRatio>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
|
||||||
public enum AspectRatio
|
public enum AspectRatio
|
||||||
{
|
{
|
||||||
Fixed4x3,
|
Fixed4x3,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<BackendThreading>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
|
||||||
public enum BackendThreading
|
public enum BackendThreading
|
||||||
{
|
{
|
||||||
Auto,
|
Auto,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<GraphicsBackend>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||||
public enum GraphicsBackend
|
public enum GraphicsBackend
|
||||||
{
|
{
|
||||||
Vulkan,
|
Vulkan,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<GraphicsDebugLevel>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))]
|
||||||
public enum GraphicsDebugLevel
|
public enum GraphicsDebugLevel
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<GamepadInputId>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))]
|
||||||
public enum GamepadInputId : byte
|
public enum GamepadInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<MotionInputBackendType>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))]
|
||||||
public enum MotionInputBackendType : byte
|
public enum MotionInputBackendType : byte
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<StickInputId>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))]
|
||||||
public enum StickInputId : byte
|
public enum StickInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@@ -5,7 +6,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
{
|
{
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
[Flags]
|
[Flags]
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<ControllerType>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
|
||||||
public enum ControllerType
|
public enum ControllerType
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<InputBackendType>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
|
||||||
public enum InputBackendType
|
public enum InputBackendType
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<Key>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<Key>))]
|
||||||
public enum Key
|
public enum Key
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<PlayerIndex>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))]
|
||||||
public enum PlayerIndex
|
public enum PlayerIndex
|
||||||
{
|
{
|
||||||
Player1 = 0,
|
Player1 = 0,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<MemoryManagerMode>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
|
||||||
public enum MemoryManagerMode : byte
|
public enum MemoryManagerMode : byte
|
||||||
{
|
{
|
||||||
SoftwarePageTable,
|
SoftwarePageTable,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<ScalingFilter>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))]
|
||||||
public enum ScalingFilter
|
public enum ScalingFilter
|
||||||
{
|
{
|
||||||
Bilinear,
|
Bilinear,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<LogClass>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<LogClass>))]
|
||||||
public enum LogClass
|
public enum LogClass
|
||||||
{
|
{
|
||||||
Application,
|
Application,
|
||||||
@@ -34,7 +35,6 @@ namespace Ryujinx.Common.Logging
|
|||||||
ServiceBsd,
|
ServiceBsd,
|
||||||
ServiceBtm,
|
ServiceBtm,
|
||||||
ServiceCaps,
|
ServiceCaps,
|
||||||
ServiceEctx,
|
|
||||||
ServiceFatal,
|
ServiceFatal,
|
||||||
ServiceFriend,
|
ServiceFriend,
|
||||||
ServiceFs,
|
ServiceFs,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<LogLevel>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))]
|
||||||
public enum LogLevel
|
public enum LogLevel
|
||||||
{
|
{
|
||||||
Debug,
|
Debug,
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ namespace Ryujinx.Common.Memory
|
|||||||
{
|
{
|
||||||
private static readonly RecyclableMemoryStreamManager _shared = new();
|
private static readonly RecyclableMemoryStreamManager _shared = new();
|
||||||
|
|
||||||
private static readonly ObjectPool<RecyclableMemoryStream> _streamPool =
|
|
||||||
new(() => new RecyclableMemoryStream(_shared, Guid.NewGuid(), null, 0));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
|
/// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
|
||||||
/// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
|
/// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
|
||||||
@@ -22,12 +19,7 @@ namespace Ryujinx.Common.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
/// <returns>A <c>RecyclableMemoryStream</c></returns>
|
||||||
public static RecyclableMemoryStream GetStream()
|
public static RecyclableMemoryStream GetStream()
|
||||||
{
|
=> new(_shared);
|
||||||
RecyclableMemoryStream stream = _streamPool.Allocate();
|
|
||||||
stream.SetLength(0);
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
|
/// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
|
||||||
@@ -63,8 +55,7 @@ namespace Ryujinx.Common.Memory
|
|||||||
RecyclableMemoryStream stream = null;
|
RecyclableMemoryStream stream = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stream = _streamPool.Allocate();
|
stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
|
||||||
stream.SetLength(0);
|
|
||||||
stream.Write(buffer);
|
stream.Write(buffer);
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
return stream;
|
return stream;
|
||||||
@@ -92,8 +83,7 @@ namespace Ryujinx.Common.Memory
|
|||||||
RecyclableMemoryStream stream = null;
|
RecyclableMemoryStream stream = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stream = _streamPool.Allocate();
|
stream = new RecyclableMemoryStream(_shared, id, tag, count);
|
||||||
stream.SetLength(0);
|
|
||||||
stream.Write(buffer, offset, count);
|
stream.Write(buffer, offset, count);
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
return stream;
|
return stream;
|
||||||
@@ -104,11 +94,6 @@ namespace Ryujinx.Common.Memory
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReleaseStream(RecyclableMemoryStream stream)
|
|
||||||
{
|
|
||||||
_streamPool.Release(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ namespace Ryujinx.Common
|
|||||||
public static string[] GetAllAvailableResources(string path, string ext = "")
|
public static string[] GetAllAvailableResources(string path, string ext = "")
|
||||||
{
|
{
|
||||||
return ResolveManifestPath(path).Item1.GetManifestResourceNames()
|
return ResolveManifestPath(path).Item1.GetManifestResourceNames()
|
||||||
.Where(r => r.StartsWith(path.Replace('/', '.')) && r.EndsWith(ext))
|
.Where(r => r.EndsWith(ext))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,21 +20,5 @@ namespace Ryujinx.Common.Utilities
|
|||||||
Debug.Assert(res != -1);
|
Debug.Assert(res != -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "dumpable" attribute of the calling process
|
|
||||||
private const int PR_SET_DUMPABLE = 4;
|
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
|
||||||
private static extern int prctl(int option, int arg2);
|
|
||||||
|
|
||||||
public static void SetCoreDumpable(bool dumpable)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
int dumpableInt = dumpable ? 1 : 0;
|
|
||||||
int result = prctl(PR_SET_DUMPABLE, dumpableInt);
|
|
||||||
Debug.Assert(result == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
37
src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#nullable enable
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>.
|
||||||
|
/// Get rid of this converter if dotnet supports similar functionality out of the box.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="TEnum">Type of enum to serialize</typeparam>
|
||||||
|
public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
|
||||||
|
{
|
||||||
|
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
string? enumValue = reader.GetString();
|
||||||
|
|
||||||
|
if (Enum.TryParse(enumValue, out TEnum value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Warning?.Print(LogClass.Configuration, $"Failed to parse enum value \"{enumValue}\" for {typeof(TEnum)}, using default \"{default(TEnum)}\"");
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
void SetRasterizerDiscard(bool discard);
|
void SetRasterizerDiscard(bool discard);
|
||||||
|
|
||||||
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
|
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
|
||||||
void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil);
|
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
|
||||||
|
|
||||||
void SetScissors(ReadOnlySpan<Rectangle<int>> regions);
|
void SetScissors(ReadOnlySpan<Rectangle<int>> regions);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||||
@@ -9,13 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||||||
{
|
{
|
||||||
public static readonly ArrayPool<ITexture> ArrayPool = ArrayPool<ITexture>.Create(512, 50);
|
public static readonly ArrayPool<ITexture> ArrayPool = ArrayPool<ITexture>.Create(512, 50);
|
||||||
public readonly CommandType CommandType => CommandType.SetRenderTargets;
|
public readonly CommandType CommandType => CommandType.SetRenderTargets;
|
||||||
private int _colorsCount;
|
|
||||||
private TableRef<ITexture[]> _colors;
|
private TableRef<ITexture[]> _colors;
|
||||||
private TableRef<ITexture> _depthStencil;
|
private TableRef<ITexture> _depthStencil;
|
||||||
|
|
||||||
public void Set(int colorsCount, TableRef<ITexture[]> colors, TableRef<ITexture> depthStencil)
|
public void Set(TableRef<ITexture[]> colors, TableRef<ITexture> depthStencil)
|
||||||
{
|
{
|
||||||
_colorsCount = colorsCount;
|
|
||||||
_colors = colors;
|
_colors = colors;
|
||||||
_depthStencil = depthStencil;
|
_depthStencil = depthStencil;
|
||||||
}
|
}
|
||||||
@@ -23,15 +20,16 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||||||
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ITexture[] colors = command._colors.Get(threaded);
|
ITexture[] colors = command._colors.Get(threaded);
|
||||||
Span<ITexture> colorsSpan = colors.AsSpan(0, command._colorsCount);
|
ITexture[] colorsCopy = ArrayPool.Rent(colors.Length);
|
||||||
|
|
||||||
for (int i = 0; i < colorsSpan.Length; i++)
|
for (int i = 0; i < colors.Length; i++)
|
||||||
{
|
{
|
||||||
colorsSpan[i] = ((ThreadedTexture)colorsSpan[i])?.Base;
|
colorsCopy[i] = ((ThreadedTexture)colors[i])?.Base;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.Pipeline.SetRenderTargets(colorsSpan, command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
|
renderer.Pipeline.SetRenderTargets(colorsCopy, command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
|
||||||
|
|
||||||
|
ArrayPool.Return(colorsCopy);
|
||||||
ArrayPool.Return(colors);
|
ArrayPool.Return(colors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,12 +267,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil)
|
public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
ITexture[] colorsCopy = SetRenderTargetsCommand.ArrayPool.Rent(colors.Length);
|
ITexture[] colorsCopy = SetRenderTargetsCommand.ArrayPool.Rent(colors.Length);
|
||||||
colors.CopyTo(colorsCopy.AsSpan());
|
colors.CopyTo(colorsCopy, 0);
|
||||||
|
|
||||||
_renderer.New<SetRenderTargetsCommand>()->Set(colors.Length, Ref(colorsCopy), Ref(depthStencil));
|
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colorsCopy), Ref(depthStencil));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ThreadedRenderer : IRenderer
|
public class ThreadedRenderer : IRenderer
|
||||||
{
|
{
|
||||||
private const int SpanPoolBytes = 8 * 1024 * 1024;
|
private const int SpanPoolBytes = 4 * 1024 * 1024;
|
||||||
private const int MaxRefsPerCommand = 2;
|
private const int MaxRefsPerCommand = 2;
|
||||||
private const int QueueCount = 10000;
|
private const int QueueCount = 10000;
|
||||||
|
|
||||||
|
|||||||
@@ -404,12 +404,9 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
if (force || _pendingSync || (syncPoint && SyncpointActions.Count > 0))
|
if (force || _pendingSync || (syncPoint && SyncpointActions.Count > 0))
|
||||||
{
|
{
|
||||||
for (int i = 0; i < SyncActions.Count; i++)
|
foreach (ISyncActionHandler action in SyncActions)
|
||||||
{
|
{
|
||||||
if (SyncActions[i].SyncPreAction(syncPoint))
|
action.SyncPreAction(syncPoint);
|
||||||
{
|
|
||||||
SyncActions.RemoveAt(i--);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ISyncActionHandler action in SyncpointActions)
|
foreach (ISyncActionHandler action in SyncpointActions)
|
||||||
|
|||||||
@@ -411,7 +411,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// flushes often enough, which is determined by the flush balance.
|
/// flushes often enough, which is determined by the flush balance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool SyncPreAction(bool syncpoint)
|
public void SyncPreAction(bool syncpoint)
|
||||||
{
|
{
|
||||||
if (syncpoint || NextSyncCopies())
|
if (syncpoint || NextSyncCopies())
|
||||||
{
|
{
|
||||||
@@ -421,8 +421,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_registeredBufferSync = _modifiedSync;
|
_registeredBufferSync = _modifiedSync;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
|
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class Buffer : INonOverlappingRange<Buffer>, ISyncActionHandler, IDisposable
|
class Buffer : INonOverlappingRange, ISyncActionHandler, IDisposable
|
||||||
{
|
{
|
||||||
private const ulong GranularBufferThreshold = 4096;
|
private const ulong GranularBufferThreshold = 4096;
|
||||||
|
|
||||||
@@ -42,9 +42,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong EndAddress => Address + Size;
|
public ulong EndAddress => Address + Size;
|
||||||
|
|
||||||
public Buffer Next { get; set; }
|
|
||||||
public Buffer Previous { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Increments when the buffer is (partially) unmapped or disposed.
|
/// Increments when the buffer is (partially) unmapped or disposed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -90,7 +87,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
private readonly bool _useGranular;
|
private readonly bool _useGranular;
|
||||||
private bool _syncActionRegistered;
|
private bool _syncActionRegistered;
|
||||||
private bool _bufferInherited;
|
|
||||||
|
|
||||||
private int _referenceCount = 1;
|
private int _referenceCount = 1;
|
||||||
|
|
||||||
@@ -117,7 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong size,
|
ulong size,
|
||||||
BufferStage stage,
|
BufferStage stage,
|
||||||
bool sparseCompatible,
|
bool sparseCompatible,
|
||||||
Buffer[] baseBuffers)
|
RangeItem<Buffer>[] baseBuffers)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_physicalMemory = physicalMemory;
|
_physicalMemory = physicalMemory;
|
||||||
@@ -138,15 +134,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
if (baseBuffers.Length != 0)
|
if (baseBuffers.Length != 0)
|
||||||
{
|
{
|
||||||
baseHandles = new List<IRegionHandle>();
|
baseHandles = new List<IRegionHandle>();
|
||||||
foreach (Buffer item in baseBuffers)
|
foreach (RangeItem<Buffer> item in baseBuffers)
|
||||||
{
|
{
|
||||||
if (item._useGranular)
|
if (item.Value._useGranular)
|
||||||
{
|
{
|
||||||
baseHandles.AddRange(item._memoryTrackingGranular.Handles);
|
baseHandles.AddRange(item.Value._memoryTrackingGranular.Handles);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
baseHandles.Add(item._memoryTracking);
|
baseHandles.Add(item.Value._memoryTracking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,14 +247,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Checks if a given range overlaps with the buffer.
|
/// Checks if a given range overlaps with the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Start address of the range</param>
|
/// <param name="address">Start address of the range</param>
|
||||||
/// <param name="endAddress">End address of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>True if the range overlaps, false otherwise</returns>
|
/// <returns>True if the range overlaps, false otherwise</returns>
|
||||||
public bool OverlapsWith(ulong address, ulong endAddress)
|
public bool OverlapsWith(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return Address < endAddress && address < EndAddress;
|
return Address < address + size && address < EndAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public INonOverlappingRange<Buffer> Split(ulong splitAddress)
|
public INonOverlappingRange Split(ulong splitAddress)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -393,16 +389,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// This will copy any buffer ranges designated for pre-flushing.
|
/// This will copy any buffer ranges designated for pre-flushing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
|
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
|
||||||
public bool SyncPreAction(bool syncpoint)
|
public void SyncPreAction(bool syncpoint)
|
||||||
{
|
{
|
||||||
if (_bufferInherited)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_referenceCount == 0)
|
if (_referenceCount == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BackingState.ShouldChangeBacking())
|
if (BackingState.ShouldChangeBacking())
|
||||||
@@ -419,8 +410,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, _syncPreRangeAction);
|
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, _syncPreRangeAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncPreRangeAction(ulong address, ulong size)
|
void SyncPreRangeAction(ulong address, ulong size)
|
||||||
@@ -437,13 +426,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
_syncActionRegistered = false;
|
_syncActionRegistered = false;
|
||||||
|
|
||||||
if (_bufferInherited)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_useGranular)
|
if (_useGranular)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
_modifiedRanges?.GetRanges(Address, Size, _syncRangeAction);
|
_modifiedRanges?.GetRanges(Address, Size, _syncRangeAction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -467,8 +453,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="from">The buffer to inherit from</param>
|
/// <param name="from">The buffer to inherit from</param>
|
||||||
public void InheritModifiedRanges(Buffer from)
|
public void InheritModifiedRanges(Buffer from)
|
||||||
{
|
{
|
||||||
from._bufferInherited = true;
|
|
||||||
|
|
||||||
if (from._modifiedRanges is { HasRanges: true })
|
if (from._modifiedRanges is { HasRanges: true })
|
||||||
{
|
{
|
||||||
if (from._syncActionRegistered && !_syncActionRegistered)
|
if (from._syncActionRegistered && !_syncActionRegistered)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="parent">Parent buffer</param>
|
/// <param name="parent">Parent buffer</param>
|
||||||
/// <param name="stage">Initial buffer stage</param>
|
/// <param name="stage">Initial buffer stage</param>
|
||||||
/// <param name="baseBuffers">Buffers to inherit state from</param>
|
/// <param name="baseBuffers">Buffers to inherit state from</param>
|
||||||
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, Buffer[] baseBuffers)
|
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, RangeItem<Buffer>[] baseBuffers)
|
||||||
{
|
{
|
||||||
_size = (int)parent.Size;
|
_size = (int)parent.Size;
|
||||||
_systemMemoryType = context.Capabilities.MemoryType;
|
_systemMemoryType = context.Capabilities.MemoryType;
|
||||||
@@ -102,9 +102,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (baseBuffers.Length != 0)
|
if (baseBuffers.Length != 0)
|
||||||
{
|
{
|
||||||
foreach (Buffer item in baseBuffers)
|
foreach (RangeItem<Buffer> item in baseBuffers)
|
||||||
{
|
{
|
||||||
CombineState(item.BackingState);
|
CombineState(item.Value.BackingState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,12 +80,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
MemoryRange subRange = range.GetSubRange(index);
|
MemoryRange subRange = range.GetSubRange(index);
|
||||||
|
|
||||||
ReadOnlySpan<Buffer> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
|
_buffers.Lock.EnterReadLock();
|
||||||
|
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
|
||||||
|
|
||||||
for (int i = 0; i < overlaps.Length; i++)
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
{
|
{
|
||||||
overlaps[i].Unmapped(subRange.Address, subRange.Size);
|
overlaps[i].Value.Unmapped(subRange.Address, subRange.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_buffers.Lock.ExitReadLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +328,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
|
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
|
||||||
ulong alignedSize = alignedEndAddress - alignedAddress;
|
ulong alignedSize = alignedEndAddress - alignedAddress;
|
||||||
|
|
||||||
Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize);
|
Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize).Value;
|
||||||
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
|
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
|
||||||
|
|
||||||
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
|
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
|
||||||
@@ -392,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (subRange.Address != MemoryManager.PteUnmapped)
|
if (subRange.Address != MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
Buffer buffer = _buffers.FindOverlap(subRange.Address, subRange.Size);
|
Buffer buffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value;
|
||||||
|
|
||||||
virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size);
|
virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size);
|
||||||
physicalBuffers.Add(buffer);
|
physicalBuffers.Add(buffer);
|
||||||
@@ -484,7 +487,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="stage">The type of usage that created the buffer</param>
|
/// <param name="stage">The type of usage that created the buffer</param>
|
||||||
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
|
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Buffer> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
Buffer newBuffer = null;
|
||||||
|
|
||||||
|
_buffers.Lock.EnterWriteLock();
|
||||||
|
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
if (overlaps.Length != 0)
|
if (overlaps.Length != 0)
|
||||||
{
|
{
|
||||||
@@ -515,7 +521,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
// Try to grow the buffer by 1.5x of its current size.
|
// Try to grow the buffer by 1.5x of its current size.
|
||||||
// This improves performance in the cases where the buffer is resized often by small amounts.
|
// This improves performance in the cases where the buffer is resized often by small amounts.
|
||||||
ulong existingSize = overlaps[0].Size;
|
ulong existingSize = overlaps[0].Value.Size;
|
||||||
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
||||||
|
|
||||||
size = Math.Max(size, growthSize);
|
size = Math.Max(size, growthSize);
|
||||||
@@ -529,22 +535,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (int i = 0; i < overlaps.Length; i++)
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
{
|
{
|
||||||
anySparseCompatible |= overlaps[i].SparseCompatible;
|
anySparseCompatible |= overlaps[i].Value.SparseCompatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer[] overlapsArray = overlaps.ToArray();
|
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
|
||||||
|
|
||||||
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
||||||
|
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
ulong newSize = endAddress - address;
|
ulong newSize = endAddress - address;
|
||||||
|
|
||||||
_buffers.Add(CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray));
|
newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
// No overlap, just create a new buffer.
|
// No overlap, just create a new buffer.
|
||||||
_buffers.Add(new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []));
|
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBuffer is not null)
|
||||||
|
{
|
||||||
|
_buffers.Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
_buffers.Add(newBuffer);
|
||||||
|
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,8 +583,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment)
|
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment)
|
||||||
{
|
{
|
||||||
bool sparseAligned = alignment >= SparseBufferAlignmentSize;
|
bool sparseAligned = alignment >= SparseBufferAlignmentSize;
|
||||||
|
Buffer newBuffer = null;
|
||||||
|
|
||||||
ReadOnlySpan<Buffer> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
_buffers.Lock.EnterWriteLock();
|
||||||
|
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
if (overlaps.Length != 0)
|
if (overlaps.Length != 0)
|
||||||
{
|
{
|
||||||
@@ -573,7 +598,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
if (overlaps[0].Address > address ||
|
if (overlaps[0].Address > address ||
|
||||||
overlaps[0].EndAddress < endAddress ||
|
overlaps[0].EndAddress < endAddress ||
|
||||||
(overlaps[0].Address & (alignment - 1)) != 0 ||
|
(overlaps[0].Address & (alignment - 1)) != 0 ||
|
||||||
(!overlaps[0].SparseCompatible && sparseAligned))
|
(!overlaps[0].Value.SparseCompatible && sparseAligned))
|
||||||
{
|
{
|
||||||
// We need to make sure the new buffer is properly aligned.
|
// We need to make sure the new buffer is properly aligned.
|
||||||
// However, after the range is aligned, it is possible that it
|
// However, after the range is aligned, it is possible that it
|
||||||
@@ -597,17 +622,34 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
ulong newSize = endAddress - address;
|
ulong newSize = endAddress - address;
|
||||||
|
|
||||||
Buffer[] overlapsArray = overlaps.ToArray();
|
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
|
||||||
|
|
||||||
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
||||||
|
|
||||||
_buffers.Add(CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray));
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
|
newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
// No overlap, just create a new buffer.
|
// No overlap, just create a new buffer.
|
||||||
_buffers.Add(new(_context, _physicalMemory, address, size, stage, sparseAligned, []));
|
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBuffer is not null)
|
||||||
|
{
|
||||||
|
_buffers.Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
_buffers.Add(newBuffer);
|
||||||
|
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,13 +663,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="stage">The type of usage that created the buffer</param>
|
/// <param name="stage">The type of usage that created the buffer</param>
|
||||||
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
|
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
|
||||||
/// <param name="overlaps">Buffers overlapping the range</param>
|
/// <param name="overlaps">Buffers overlapping the range</param>
|
||||||
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, Buffer[] overlaps)
|
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, RangeItem<Buffer>[] overlaps)
|
||||||
{
|
{
|
||||||
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps);
|
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps);
|
||||||
|
|
||||||
for (int index = 0; index < overlaps.Length; index++)
|
for (int index = 0; index < overlaps.Length; index++)
|
||||||
{
|
{
|
||||||
Buffer buffer = overlaps[index];
|
Buffer buffer = overlaps[index].Value;
|
||||||
|
|
||||||
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
||||||
|
|
||||||
@@ -855,7 +897,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
MemoryRange subRange = range.GetSubRange(i);
|
MemoryRange subRange = range.GetSubRange(i);
|
||||||
|
|
||||||
Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size);
|
Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value;
|
||||||
|
|
||||||
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
|
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
|
||||||
|
|
||||||
@@ -903,7 +945,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
buffer = _buffers.FindOverlap(address, size);
|
buffer = _buffers.FindOverlap(address, size).Value;
|
||||||
|
|
||||||
buffer.CopyFromDependantVirtualBuffers();
|
buffer.CopyFromDependantVirtualBuffers();
|
||||||
buffer.SynchronizeMemory(address, size);
|
buffer.SynchronizeMemory(address, size);
|
||||||
@@ -915,7 +957,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer = _buffers.FindOverlapFast(address, 1);
|
buffer = _buffers.FindOverlapFast(address, 1).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
@@ -953,7 +995,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
Buffer buffer = _buffers.FindOverlap(address, size);
|
Buffer buffer = _buffers.FindOverlap(address, size).Value;
|
||||||
|
|
||||||
if (copyBackVirtual)
|
if (copyBackVirtual)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A range within a buffer that has been modified by the GPU.
|
/// A range within a buffer that has been modified by the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class BufferModifiedRange : INonOverlappingRange<BufferModifiedRange>
|
class BufferModifiedRange : INonOverlappingRange
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start address of the range in guest memory.
|
/// Start address of the range in guest memory.
|
||||||
@@ -25,9 +25,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong EndAddress => Address + Size;
|
public ulong EndAddress => Address + Size;
|
||||||
|
|
||||||
public BufferModifiedRange Next { get; set; }
|
|
||||||
public BufferModifiedRange Previous { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The GPU sync number at the time of the last modification.
|
/// The GPU sync number at the time of the last modification.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -57,14 +54,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Checks if a given range overlaps with the modified range.
|
/// Checks if a given range overlaps with the modified range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Start address of the range</param>
|
/// <param name="address">Start address of the range</param>
|
||||||
/// <param name="endAddress">End address of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>True if the range overlaps, false otherwise</returns>
|
/// <returns>True if the range overlaps, false otherwise</returns>
|
||||||
public bool OverlapsWith(ulong address, ulong endAddress)
|
public bool OverlapsWith(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return Address < endAddress && address < EndAddress;
|
return Address < address + size && address < EndAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public INonOverlappingRange<BufferModifiedRange> Split(ulong splitAddress)
|
public INonOverlappingRange Split(ulong splitAddress)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -122,11 +119,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Slices a given region using the modified regions in the list. Calls the action for the new slices.
|
// Slices a given region using the modified regions in the list. Calls the action for the new slices.
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
|
|
||||||
ReadOnlySpan<BufferModifiedRange> overlaps = FindOverlapsAsSpan(address, size);
|
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
for (int i = 0; i < overlaps.Length; i++)
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps[i];
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
|
|
||||||
if (overlap.Address > address)
|
if (overlap.Address > address)
|
||||||
{
|
{
|
||||||
@@ -160,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong syncNumber = _context.SyncNumber;
|
ulong syncNumber = _context.SyncNumber;
|
||||||
// We may overlap with some existing modified regions. They must be cut into by the new entry.
|
// We may overlap with some existing modified regions. They must be cut into by the new entry.
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
(BufferModifiedRange first, BufferModifiedRange last) = FindOverlapsAsNodes(address, size);
|
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
|
||||||
|
|
||||||
if (first is null)
|
if (first is null)
|
||||||
{
|
{
|
||||||
@@ -173,39 +170,34 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (first.Address == address && first.EndAddress == endAddress)
|
if (first.Address == address && first.EndAddress == endAddress)
|
||||||
{
|
{
|
||||||
first.SyncNumber = syncNumber;
|
first.Value.SyncNumber = syncNumber;
|
||||||
first.Parent = this;
|
first.Value.Parent = this;
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first.Address < address)
|
if (first.Address < address)
|
||||||
{
|
{
|
||||||
|
first.Value.Size = address - first.Address;
|
||||||
|
Update(first);
|
||||||
|
|
||||||
if (first.EndAddress > endAddress)
|
if (first.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
|
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
|
||||||
first.SyncNumber, first.Parent));
|
first.Value.SyncNumber, first.Value.Parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
first.Size = address - first.Address;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (first.EndAddress > endAddress)
|
if (first.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
first.Size = first.EndAddress - endAddress;
|
first.Value.Size = first.EndAddress - endAddress;
|
||||||
first.Address = endAddress;
|
first.Value.Address = endAddress;
|
||||||
|
Update(first);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
first.Address = address;
|
Remove(first.Value);
|
||||||
first.Size = size;
|
|
||||||
first.SyncNumber = syncNumber;
|
|
||||||
first.Parent = this;
|
|
||||||
|
|
||||||
Lock.ExitWriteLock();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,39 +207,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferModifiedRange buffPre = null;
|
||||||
|
BufferModifiedRange buffPost = null;
|
||||||
|
bool extendsPost = false;
|
||||||
|
bool extendsPre = false;
|
||||||
|
|
||||||
if (first.Address < address)
|
if (first.Address < address)
|
||||||
{
|
{
|
||||||
first.Size = address - first.Address;
|
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
|
||||||
first = first.Next;
|
first.Value.SyncNumber, first.Value.Parent);
|
||||||
|
extendsPre = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last.EndAddress > endAddress)
|
if (last.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
last.Size = last.EndAddress - endAddress;
|
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
|
||||||
last.Address = endAddress;
|
last.Value.SyncNumber, last.Value.Parent);
|
||||||
last = last.Previous;
|
extendsPost = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first.Address < last.Address)
|
RemoveRange(first, last);
|
||||||
|
|
||||||
|
if (extendsPre)
|
||||||
{
|
{
|
||||||
RemoveRange(first.Next, last);
|
Add(buffPre);
|
||||||
first.Address = address;
|
|
||||||
first.Size = size;
|
|
||||||
first.SyncNumber = syncNumber;
|
|
||||||
first.Parent = this;
|
|
||||||
}
|
|
||||||
else if (first.Address == last.Address)
|
|
||||||
{
|
|
||||||
first.Address = address;
|
|
||||||
first.Size = size;
|
|
||||||
first.SyncNumber = syncNumber;
|
|
||||||
first.Parent = this;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Add(new BufferModifiedRange(address, size, syncNumber, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extendsPost)
|
||||||
|
{
|
||||||
|
Add(buffPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
Add(new BufferModifiedRange(address, size, syncNumber, this));
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,11 +252,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
|
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
|
||||||
{
|
{
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
ReadOnlySpan<BufferModifiedRange> overlaps = FindOverlapsAsSpan(address, size);
|
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
for (int i = 0; i < overlaps.Length; i++)
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps[i];
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
|
|
||||||
if (overlap.SyncNumber == syncNumber)
|
if (overlap.SyncNumber == syncNumber)
|
||||||
{
|
{
|
||||||
@@ -286,18 +277,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
BufferModifiedRange[] overlaps = FindOverlapsAsArray(address, size, out int length);
|
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int length);
|
||||||
Lock.ExitReadLock();
|
Lock.ExitReadLock();
|
||||||
|
|
||||||
if (length != 0)
|
if (length != 0)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps[i];
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
rangeAction(overlap.Address, overlap.Size);
|
rangeAction(overlap.Address, overlap.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<BufferModifiedRange>.Shared.Return(overlaps);
|
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +301,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public bool HasRange(ulong address, ulong size)
|
public bool HasRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
BufferModifiedRange first = FindOverlapFast(address, size);
|
RangeItem<BufferModifiedRange> first = FindOverlapFast(address, size);
|
||||||
bool result = first is not null;
|
bool result = first is not null;
|
||||||
Lock.ExitReadLock();
|
Lock.ExitReadLock();
|
||||||
return result;
|
return result;
|
||||||
@@ -345,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="address">The start address of the flush range</param>
|
/// <param name="address">The start address of the flush range</param>
|
||||||
/// <param name="endAddress">The end address of the flush range</param>
|
/// <param name="endAddress">The end address of the flush range</param>
|
||||||
private void RemoveRangesAndFlush(
|
private void RemoveRangesAndFlush(
|
||||||
BufferModifiedRange[] overlaps,
|
RangeItem<BufferModifiedRange>[] overlaps,
|
||||||
int rangeCount,
|
int rangeCount,
|
||||||
long highestDiff,
|
long highestDiff,
|
||||||
ulong currentSync,
|
ulong currentSync,
|
||||||
@@ -358,7 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (int i = 0; i < rangeCount; i++)
|
for (int i = 0; i < rangeCount; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps[i];
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
|
|
||||||
long diff = (long)(overlap.SyncNumber - currentSync);
|
long diff = (long)(overlap.SyncNumber - currentSync);
|
||||||
|
|
||||||
@@ -367,14 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong clampAddress = Math.Max(address, overlap.Address);
|
ulong clampAddress = Math.Max(address, overlap.Address);
|
||||||
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
|
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
|
||||||
|
|
||||||
if (i == 0 || i == rangeCount - 1)
|
ClearPart(overlap, clampAddress, clampEnd);
|
||||||
{
|
|
||||||
ClearPart(overlap, clampAddress, clampEnd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Remove(overlap);
|
|
||||||
}
|
|
||||||
|
|
||||||
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction);
|
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction);
|
||||||
}
|
}
|
||||||
@@ -414,7 +398,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
||||||
BufferModifiedRange[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount);
|
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount);
|
||||||
|
|
||||||
if (rangeCount == 0)
|
if (rangeCount == 0)
|
||||||
{
|
{
|
||||||
@@ -430,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (int i = 0; i < rangeCount; i++)
|
for (int i = 0; i < rangeCount; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps![i];
|
BufferModifiedRange overlap = overlaps![i].Value;
|
||||||
|
|
||||||
long diff = (long)(overlap.SyncNumber - currentSync);
|
long diff = (long)(overlap.SyncNumber - currentSync);
|
||||||
|
|
||||||
@@ -452,7 +436,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
|
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
|
||||||
|
|
||||||
ArrayPool<BufferModifiedRange>.Shared.Return(overlaps!);
|
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps!);
|
||||||
|
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
@@ -468,9 +452,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction)
|
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction)
|
||||||
{
|
{
|
||||||
ranges.Lock.EnterReadLock();
|
ranges.Lock.EnterReadLock();
|
||||||
int rangesCount = ranges.Count;
|
BufferModifiedRange[] inheritRanges = ranges.ToArray();
|
||||||
BufferModifiedRange[] inheritRanges = ArrayPool<BufferModifiedRange>.Shared.Rent(ranges.Count);
|
|
||||||
ranges.Items.AsSpan(0, ranges.Count).CopyTo(inheritRanges);
|
|
||||||
ranges.Lock.ExitReadLock();
|
ranges.Lock.ExitReadLock();
|
||||||
|
|
||||||
// Copy over the migration from the previous range list
|
// Copy over the migration from the previous range list
|
||||||
@@ -497,25 +479,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
|
|
||||||
for (int i = 0; i < rangesCount; i++)
|
foreach (BufferModifiedRange range in inheritRanges)
|
||||||
{
|
{
|
||||||
BufferModifiedRange range = inheritRanges[i];
|
|
||||||
Add(range);
|
Add(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
|
|
||||||
ulong currentSync = _context.SyncNumber;
|
ulong currentSync = _context.SyncNumber;
|
||||||
for (int i = 0; i < rangesCount; i++)
|
foreach (BufferModifiedRange range in inheritRanges)
|
||||||
{
|
{
|
||||||
BufferModifiedRange range = inheritRanges[i];
|
|
||||||
if (range.SyncNumber != currentSync)
|
if (range.SyncNumber != currentSync)
|
||||||
{
|
{
|
||||||
registerRangeAction(range.Address, range.Size);
|
registerRangeAction(range.Address, range.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<BufferModifiedRange>.Shared.Return(inheritRanges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -556,25 +534,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
private void ClearPart(BufferModifiedRange overlap, ulong address, ulong endAddress)
|
private void ClearPart(BufferModifiedRange overlap, ulong address, ulong endAddress)
|
||||||
{
|
{
|
||||||
|
Remove(overlap);
|
||||||
|
|
||||||
// If the overlap extends outside of the clear range, make sure those parts still exist.
|
// If the overlap extends outside of the clear range, make sure those parts still exist.
|
||||||
|
|
||||||
if (overlap.Address < address)
|
if (overlap.Address < address)
|
||||||
{
|
{
|
||||||
if (overlap.EndAddress > endAddress)
|
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent));
|
||||||
{
|
}
|
||||||
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
overlap.Size = address - overlap.Address;
|
if (overlap.EndAddress > endAddress)
|
||||||
}
|
|
||||||
else if (overlap.EndAddress > endAddress)
|
|
||||||
{
|
{
|
||||||
overlap.Size = overlap.EndAddress - endAddress;
|
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
|
||||||
overlap.Address = endAddress;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Remove(overlap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,7 +558,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
(BufferModifiedRange first, BufferModifiedRange last) = FindOverlapsAsNodes(address, size);
|
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
|
||||||
|
|
||||||
if (first is null)
|
if (first is null)
|
||||||
{
|
{
|
||||||
@@ -599,24 +570,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (first.Address < address)
|
if (first.Address < address)
|
||||||
{
|
{
|
||||||
first.Size = address - first.Address;
|
first.Value.Size = address - first.Address;
|
||||||
|
Update(first);
|
||||||
|
|
||||||
if (first.EndAddress > endAddress)
|
if (first.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
|
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
|
||||||
first.SyncNumber, first.Parent));
|
first.Value.SyncNumber, first.Value.Parent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (first.EndAddress > endAddress)
|
if (first.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
first.Size = first.EndAddress - endAddress;
|
first.Value.Size = first.EndAddress - endAddress;
|
||||||
first.Address = endAddress;
|
first.Value.Address = endAddress;
|
||||||
|
Update(first);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Remove(first);
|
Remove(first.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,14 +605,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
if (first.Address < address)
|
if (first.Address < address)
|
||||||
{
|
{
|
||||||
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
|
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
|
||||||
first.SyncNumber, first.Parent);
|
first.Value.SyncNumber, first.Value.Parent);
|
||||||
extendsPre = true;
|
extendsPre = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last.EndAddress > endAddress)
|
if (last.EndAddress > endAddress)
|
||||||
{
|
{
|
||||||
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
|
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
|
||||||
last.SyncNumber, last.Parent);
|
last.Value.SyncNumber, last.Value.Parent);
|
||||||
extendsPost = true;
|
extendsPost = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
ICounterEvent evt = _items[index + i].Event;
|
ICounterEvent evt = _items[index + i].Event;
|
||||||
evt?.Invalid = true;
|
if (evt != null)
|
||||||
|
{
|
||||||
|
evt.Invalid = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_items.RemoveRange(index, count);
|
_items.RemoveRange(index, count);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a GPU virtual memory range.
|
/// Represents a GPU virtual memory range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class VirtualRange : INonOverlappingRange<VirtualRange>
|
private class VirtualRange : INonOverlappingRange
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU virtual address where the range starts.
|
/// GPU virtual address where the range starts.
|
||||||
@@ -32,9 +32,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong EndAddress => Address + Size;
|
public ulong EndAddress => Address + Size;
|
||||||
|
|
||||||
public VirtualRange Next { get; set; }
|
|
||||||
public VirtualRange Previous { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical regions where the GPU virtual region is mapped.
|
/// Physical regions where the GPU virtual region is mapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -57,14 +54,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Checks if a given range overlaps with the buffer.
|
/// Checks if a given range overlaps with the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Start address of the range</param>
|
/// <param name="address">Start address of the range</param>
|
||||||
/// <param name="endAddress">End address of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>True if the range overlaps, false otherwise</returns>
|
/// <returns>True if the range overlaps, false otherwise</returns>
|
||||||
public bool OverlapsWith(ulong address, ulong endAddress)
|
public bool OverlapsWith(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return Address < endAddress && address < EndAddress;
|
return Address < address + size && address < EndAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public INonOverlappingRange<VirtualRange> Split(ulong splitAddress)
|
public INonOverlappingRange Split(ulong splitAddress)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -125,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong originalVa = gpuVa;
|
ulong originalVa = gpuVa;
|
||||||
|
|
||||||
_virtualRanges.Lock.EnterWriteLock();
|
_virtualRanges.Lock.EnterWriteLock();
|
||||||
(VirtualRange first, VirtualRange last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
|
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
|
||||||
|
|
||||||
if (first is not null)
|
if (first is not null)
|
||||||
{
|
{
|
||||||
@@ -150,8 +147,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
found = first.Range.Count == 1 || IsSparseAligned(first.Range);
|
found = first.Value.Range.Count == 1 || IsSparseAligned(first.Value.Range);
|
||||||
range = first.Range.Slice(gpuVa - first.Address, size);
|
range = first.Value.Range.Slice(gpuVa - first.Address, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
/// Action to be performed immediately before sync is created.
|
/// Action to be performed immediately before sync is created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
|
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
|
||||||
bool SyncPreAction(bool syncpoint) { return true; }
|
void SyncPreAction(bool syncpoint) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1166,7 +1166,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil)
|
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
EnsureFramebuffer();
|
EnsureFramebuffer();
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// - Both branches are jumping to the same location.
|
// - Both branches are jumping to the same location.
|
||||||
// In this case, the branch on the current block can be removed,
|
// In this case, the branch on the current block can be removed,
|
||||||
// as the next block is going to jump to the same place anyway.
|
// as the next block is going to jump to the same place anyway.
|
||||||
|
if (nextBlock == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (nextBlock?.Operations.First?.Value is not Operation next)
|
if (nextBlock.Operations.First?.Value is not Operation next)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,31 +71,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
HasDepthStencil = isDepthStencil;
|
HasDepthStencil = isDepthStencil;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FramebufferParams(Device device, ReadOnlySpan<ITexture> colors, ITexture depthStencil)
|
public FramebufferParams(Device device, ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
|
|
||||||
int colorsCount = 0;
|
int colorsCount = colors.Count(IsValidTextureView);
|
||||||
_colorsCanonical = new TextureView[colors.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < colors.Length; i++)
|
|
||||||
{
|
|
||||||
ITexture color = colors[i];
|
|
||||||
if (color is TextureView { Valid: true } view)
|
|
||||||
{
|
|
||||||
colorsCount++;
|
|
||||||
_colorsCanonical[i] = view;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_colorsCanonical[i] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
||||||
|
|
||||||
_attachments = new Auto<DisposableImageView>[count];
|
_attachments = new Auto<DisposableImageView>[count];
|
||||||
_colors = new TextureView[colorsCount];
|
_colors = new TextureView[colorsCount];
|
||||||
|
_colorsCanonical = colors.Select(color => color is TextureView view && view.Valid ? view : null).ToArray();
|
||||||
|
|
||||||
AttachmentSamples = new uint[count];
|
AttachmentSamples = new uint[count];
|
||||||
AttachmentFormats = new VkFormat[count];
|
AttachmentFormats = new VkFormat[count];
|
||||||
@@ -179,17 +165,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_totalCount = colors.Length;
|
_totalCount = colors.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FramebufferParams Update(ReadOnlySpan<ITexture> colors, ITexture depthStencil)
|
public FramebufferParams Update(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
int colorsCount = 0;
|
int colorsCount = colors.Count(IsValidTextureView);
|
||||||
|
|
||||||
foreach (ITexture color in colors)
|
|
||||||
{
|
|
||||||
if (IsValidTextureView(color))
|
|
||||||
{
|
|
||||||
colorsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -10,8 +10,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class MultiFenceHolder
|
class MultiFenceHolder
|
||||||
{
|
{
|
||||||
public static readonly ObjectPool<FenceHolder[]> FencePool = new(() => new FenceHolder[CommandBufferPool.MaxCommandBuffers]);
|
|
||||||
|
|
||||||
private const int BufferUsageTrackingGranularity = 4096;
|
private const int BufferUsageTrackingGranularity = 4096;
|
||||||
|
|
||||||
public FenceHolder[] Fences { get; }
|
public FenceHolder[] Fences { get; }
|
||||||
@@ -22,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public MultiFenceHolder()
|
public MultiFenceHolder()
|
||||||
{
|
{
|
||||||
Fences = FencePool.Allocate();
|
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
/// <param name="size">Size of the buffer</param>
|
/// <param name="size">Size of the buffer</param>
|
||||||
public MultiFenceHolder(int size)
|
public MultiFenceHolder(int size)
|
||||||
{
|
{
|
||||||
Fences = FencePool.Allocate();
|
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||||
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1035,7 +1035,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetRenderTargetsInternal(Span<ITexture> colors, ITexture depthStencil, bool filterWriteMasked)
|
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
|
||||||
{
|
{
|
||||||
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
|
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
|
||||||
CreateRenderPass();
|
CreateRenderPass();
|
||||||
@@ -1043,7 +1043,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalAttachmentChange();
|
SignalAttachmentChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil)
|
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
_framebufferUsingColorWriteMask = false;
|
_framebufferUsingColorWriteMask = false;
|
||||||
SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
|
SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
|
||||||
@@ -1389,7 +1389,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_currentPipelineHandle = 0;
|
_currentPipelineHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateFramebuffer(Span<ITexture> colors, ITexture depthStencil, bool filterWriteMasked)
|
private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
|
||||||
{
|
{
|
||||||
if (filterWriteMasked)
|
if (filterWriteMasked)
|
||||||
{
|
{
|
||||||
@@ -1399,7 +1399,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
// Just try to remove duplicate attachments.
|
// Just try to remove duplicate attachments.
|
||||||
// Save a copy of the array to rebind when mask changes.
|
// Save a copy of the array to rebind when mask changes.
|
||||||
|
|
||||||
void MaskOut(ReadOnlySpan<ITexture> colors)
|
void MaskOut()
|
||||||
{
|
{
|
||||||
if (!_framebufferUsingColorWriteMask)
|
if (!_framebufferUsingColorWriteMask)
|
||||||
{
|
{
|
||||||
@@ -1436,12 +1436,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (vkBlend.ColorWriteMask == 0)
|
if (vkBlend.ColorWriteMask == 0)
|
||||||
{
|
{
|
||||||
colors[i] = null;
|
colors[i] = null;
|
||||||
MaskOut(colors);
|
MaskOut();
|
||||||
}
|
}
|
||||||
else if (vkBlend2.ColorWriteMask == 0)
|
else if (vkBlend2.ColorWriteMask == 0)
|
||||||
{
|
{
|
||||||
colors[j] = null;
|
colors[j] = null;
|
||||||
MaskOut(colors);
|
MaskOut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -193,8 +193,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_firstHandle = first.ID + 1;
|
_firstHandle = first.ID + 1;
|
||||||
_handles.RemoveAt(0);
|
_handles.RemoveAt(0);
|
||||||
Array.Clear(first.Waitable.Fences);
|
ArrayPool<FenceHolder>.Shared.Return(first.Waitable.Fences);
|
||||||
MultiFenceHolder.FencePool.Release(first.Waitable.Fences);
|
|
||||||
first.Waitable = null;
|
first.Waitable = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +1,43 @@
|
|||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Debugger
|
namespace Ryujinx.HLE.Debugger
|
||||||
{
|
{
|
||||||
public partial class Debugger
|
public partial class Debugger
|
||||||
{
|
{
|
||||||
private sealed record RcmdEntry(string[] Names, Func<Debugger, string, string> Handler, string[] HelpLines);
|
|
||||||
|
|
||||||
// Atmosphere/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp
|
|
||||||
private static readonly string[] _memoryStateNames =
|
|
||||||
{
|
|
||||||
"----- Free -----",
|
|
||||||
"Io ",
|
|
||||||
"Static ",
|
|
||||||
"Code ",
|
|
||||||
"CodeData ",
|
|
||||||
"Normal ",
|
|
||||||
"Shared ",
|
|
||||||
"Alias ",
|
|
||||||
"AliasCode ",
|
|
||||||
"AliasCodeData ",
|
|
||||||
"Ipc ",
|
|
||||||
"Stack ",
|
|
||||||
"ThreadLocal ",
|
|
||||||
"Transfered ",
|
|
||||||
"SharedTransfered",
|
|
||||||
"SharedCode ",
|
|
||||||
"Inaccessible ",
|
|
||||||
"NonSecureIpc ",
|
|
||||||
"NonDeviceIpc ",
|
|
||||||
"Kernel ",
|
|
||||||
"GeneratedCode ",
|
|
||||||
"CodeOut ",
|
|
||||||
"Coverage ",
|
|
||||||
};
|
|
||||||
|
|
||||||
static Debugger()
|
static Debugger()
|
||||||
{
|
{
|
||||||
_rcmdDelegates.Add(new RcmdEntry(
|
_rcmdDelegates.Add(["help"],
|
||||||
["help"],
|
_ => _rcmdDelegates.Keys
|
||||||
(dbgr, _) => _rcmdDelegates
|
.Where(x => !x[0].Equals("help"))
|
||||||
.Where(entry => entry.HelpLines.Length > 0)
|
.Select(x => x.JoinToString('\n'))
|
||||||
.SelectMany(entry => entry.HelpLines)
|
.JoinToString('\n') + '\n'
|
||||||
.JoinToString('\n') + '\n',
|
);
|
||||||
Array.Empty<string>()));
|
_rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo());
|
||||||
|
_rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace());
|
||||||
_rcmdDelegates.Add(new RcmdEntry(["get info"], (dbgr, _) => dbgr.GetProcessInfo(), ["get info"]));
|
_rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters());
|
||||||
_rcmdDelegates.Add(new RcmdEntry(["backtrace", "bt"], (dbgr, _) => dbgr.GetStackTrace(), ["backtrace", "bt"]));
|
_rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump());
|
||||||
_rcmdDelegates.Add(new RcmdEntry(["registers", "reg"], (dbgr, _) => dbgr.GetRegisters(), ["registers", "reg"]));
|
|
||||||
_rcmdDelegates.Add(new RcmdEntry(["minidump"], (dbgr, _) => dbgr.GetMinidump(), ["minidump"]));
|
|
||||||
_rcmdDelegates.Add(new RcmdEntry(["get mappings"], (dbgr, args) => dbgr.GetMemoryMappings(args), ["get mappings", "get mappings {address}"]));
|
|
||||||
_rcmdDelegates.Add(new RcmdEntry(["get mapping"], (dbgr, args) => dbgr.GetMemoryMapping(args), ["get mapping {address}"]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly List<RcmdEntry> _rcmdDelegates = [];
|
private static readonly Dictionary<string[], Func<Debugger, string>> _rcmdDelegates = new();
|
||||||
|
|
||||||
public static string CallRcmdDelegate(Debugger debugger, string command)
|
public static Func<Debugger, string> FindRcmdDelegate(string command)
|
||||||
{
|
{
|
||||||
string originalCommand = command ?? string.Empty;
|
Func<Debugger, string> searchResult = _ => $"Unknown command: {command}\n";
|
||||||
string trimmedCommand = originalCommand.Trim();
|
|
||||||
|
|
||||||
foreach (RcmdEntry entry in _rcmdDelegates)
|
foreach ((string[] names, Func<Debugger, string> dlg) in _rcmdDelegates)
|
||||||
{
|
{
|
||||||
foreach (string name in entry.Names)
|
if (names.ContainsIgnoreCase(command.Trim()))
|
||||||
{
|
{
|
||||||
if (trimmedCommand.Equals(name, StringComparison.OrdinalIgnoreCase))
|
searchResult = dlg;
|
||||||
{
|
break;
|
||||||
return entry.Handler(debugger, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmedCommand.Length > name.Length &&
|
|
||||||
trimmedCommand.StartsWith(name, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
char.IsWhiteSpace(trimmedCommand[name.Length]))
|
|
||||||
{
|
|
||||||
string arguments = trimmedCommand[name.Length..].TrimStart();
|
|
||||||
return entry.Handler(debugger, arguments);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"Unknown command: {originalCommand}\n";
|
return searchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetStackTrace()
|
public string GetStackTrace()
|
||||||
@@ -134,181 +86,5 @@ namespace Ryujinx.HLE.Debugger
|
|||||||
return $"Error getting process info: {e.Message}\n";
|
return $"Error getting process info: {e.Message}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetMemoryMappings(string arguments)
|
|
||||||
{
|
|
||||||
if (Process?.MemoryManager is not { } memoryManager)
|
|
||||||
{
|
|
||||||
return "No application process found\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string trimmedArgs = arguments?.Trim() ?? string.Empty;
|
|
||||||
|
|
||||||
ulong startAddress = 0;
|
|
||||||
if (!string.IsNullOrEmpty(trimmedArgs))
|
|
||||||
{
|
|
||||||
if (!TryParseAddressArgument(trimmedArgs, out startAddress))
|
|
||||||
{
|
|
||||||
return $"Invalid address: {trimmedArgs}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong requestedAddress = startAddress;
|
|
||||||
ulong currentAddress = Math.Max(requestedAddress, memoryManager.AddrSpaceStart);
|
|
||||||
StringBuilder sb = new();
|
|
||||||
sb.AppendLine($"Mappings (starting from 0x{requestedAddress:x10}):");
|
|
||||||
|
|
||||||
if (currentAddress >= memoryManager.AddrSpaceEnd)
|
|
||||||
{
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (currentAddress < memoryManager.AddrSpaceEnd)
|
|
||||||
{
|
|
||||||
KMemoryInfo info = memoryManager.QueryMemory(currentAddress);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (info.Size == 0 || info.Address >= memoryManager.AddrSpaceEnd)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendLine(FormatMapping(info, indent: true));
|
|
||||||
|
|
||||||
if (info.Address > ulong.MaxValue - info.Size)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong nextAddress = info.Address + info.Size;
|
|
||||||
if (nextAddress <= currentAddress)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAddress = nextAddress;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
KMemoryInfo.Pool.Release(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetMemoryMapping(string arguments)
|
|
||||||
{
|
|
||||||
if (Process?.MemoryManager is not { } memoryManager)
|
|
||||||
{
|
|
||||||
return "No application process found\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string trimmedArgs = arguments?.Trim() ?? string.Empty;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(trimmedArgs))
|
|
||||||
{
|
|
||||||
return "Missing address argument for `get mapping`\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseAddressArgument(trimmedArgs, out ulong address))
|
|
||||||
{
|
|
||||||
return $"Invalid address: {trimmedArgs}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
KMemoryInfo info = memoryManager.QueryMemory(address);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return FormatMapping(info, indent: false) + '\n';
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
KMemoryInfo.Pool.Release(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatMapping(KMemoryInfo info, bool indent)
|
|
||||||
{
|
|
||||||
ulong endAddress;
|
|
||||||
|
|
||||||
if (info.Size == 0)
|
|
||||||
{
|
|
||||||
endAddress = info.Address;
|
|
||||||
}
|
|
||||||
else if (info.Address > ulong.MaxValue - (info.Size - 1))
|
|
||||||
{
|
|
||||||
endAddress = ulong.MaxValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
endAddress = info.Address + info.Size - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
string prefix = indent ? " " : string.Empty;
|
|
||||||
return $"{prefix}0x{info.Address:x10} - 0x{endAddress:x10} {GetPermissionString(info)} {GetMemoryStateName(info.State)} {GetAttributeFlags(info)} [{info.IpcRefCount}, {info.DeviceRefCount}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetPermissionString(KMemoryInfo info)
|
|
||||||
{
|
|
||||||
if ((info.State & MemoryState.UserMask) == MemoryState.Unmapped)
|
|
||||||
{
|
|
||||||
return " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
return info.Permission switch
|
|
||||||
{
|
|
||||||
KMemoryPermission.ReadAndExecute => "r-x",
|
|
||||||
KMemoryPermission.Read => "r--",
|
|
||||||
KMemoryPermission.ReadAndWrite => "rw-",
|
|
||||||
_ => "---"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetMemoryStateName(MemoryState state)
|
|
||||||
{
|
|
||||||
int stateIndex = (int)(state & MemoryState.UserMask);
|
|
||||||
if ((uint)stateIndex < _memoryStateNames.Length)
|
|
||||||
{
|
|
||||||
return _memoryStateNames[stateIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Unknown ";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryParseAddressArgument(string text, out ulong value)
|
|
||||||
{
|
|
||||||
value = 0;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string trimmed = text.Trim();
|
|
||||||
|
|
||||||
if (trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
trimmed = trimmed[2..];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmed.Length == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ulong.TryParse(trimmed, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetAttributeFlags(KMemoryInfo info)
|
|
||||||
{
|
|
||||||
char locked = info.Attribute.HasFlag(MemoryAttribute.Borrowed) ? 'L' : '-';
|
|
||||||
char ipc = info.Attribute.HasFlag(MemoryAttribute.IpcMapped) ? 'I' : '-';
|
|
||||||
char device = info.Attribute.HasFlag(MemoryAttribute.DeviceMapped) ? 'D' : '-';
|
|
||||||
char uncached = info.Attribute.HasFlag(MemoryAttribute.Uncached) ? 'U' : '-';
|
|
||||||
|
|
||||||
return $"{locked}{ipc}{device}{uncached}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,8 +404,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
|
|||||||
string command = Helpers.FromHex(hexCommand);
|
string command = Helpers.FromHex(hexCommand);
|
||||||
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
|
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
|
||||||
|
|
||||||
string response = Debugger.CallRcmdDelegate(Debugger, command);
|
Func<Debugger, string> rcmd = Debugger.FindRcmdDelegate(command);
|
||||||
Processor.ReplyHex(response);
|
|
||||||
|
Processor.ReplyHex(rcmd(Debugger));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -483,29 +483,10 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
if (Directory.Exists(keysSource))
|
if (Directory.Exists(keysSource))
|
||||||
{
|
{
|
||||||
string[] keyPaths = Directory.EnumerateFiles(keysSource, "*.keys").ToArray();
|
foreach (string filePath in Directory.EnumerateFiles(keysSource, "*.keys"))
|
||||||
|
|
||||||
if (keyPaths.Length is 0)
|
|
||||||
throw new FileNotFoundException($"Directory '{keysSource}' contained no '.keys' files.");
|
|
||||||
|
|
||||||
foreach (string filePath in keyPaths)
|
|
||||||
{
|
{
|
||||||
try
|
VerifyKeysFile(filePath);
|
||||||
{
|
File.Copy(filePath, Path.Combine(installDirectory, Path.GetFileName(filePath)), true);
|
||||||
VerifyKeysFile(filePath);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, e.Message);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string destPath = Path.Combine(installDirectory, Path.GetFileName(filePath));
|
|
||||||
|
|
||||||
if (File.Exists(destPath))
|
|
||||||
File.Delete(destPath);
|
|
||||||
|
|
||||||
File.Copy(filePath, destPath, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -520,25 +501,13 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
using FileStream file = File.OpenRead(keysSource);
|
using FileStream file = File.OpenRead(keysSource);
|
||||||
|
|
||||||
if (info.Extension is not ".keys")
|
if (info.Extension is ".keys")
|
||||||
throw new InvalidFirmwarePackageException("Input file extension is not .keys");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
VerifyKeysFile(keysSource);
|
VerifyKeysFile(keysSource);
|
||||||
|
File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
|
||||||
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
|
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
|
||||||
}
|
|
||||||
|
|
||||||
string dest = Path.Combine(installDirectory, info.Name);
|
|
||||||
|
|
||||||
if (File.Exists(dest))
|
|
||||||
File.Delete(dest);
|
|
||||||
|
|
||||||
// overwrite: true seems to not work on its own? https://github.com/Ryubing/Issues/issues/189
|
|
||||||
File.Copy(keysSource, dest, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
|
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
|
||||||
@@ -1016,8 +985,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
public static void VerifyKeysFile(string filePath)
|
public static void VerifyKeysFile(string filePath)
|
||||||
{
|
{
|
||||||
// Verify the keys file format refers to https://github.com/Thealexbarney/LibHac/blob/master/KEYS.md
|
// Verify the keys file format refers to https://github.com/Thealexbarney/LibHac/blob/master/KEYS.md
|
||||||
string genericPattern = "^[a-z0-9_]+ = [a-z0-9]+$";
|
string genericPattern = @"^[a-z0-9_]+ = [a-z0-9]+$";
|
||||||
string titlePattern = "^[a-z0-9]{32} = [a-z0-9]{32}$";
|
string titlePattern = @"^[a-z0-9]{32} = [a-z0-9]{32}$";
|
||||||
|
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
{
|
{
|
||||||
@@ -1025,13 +994,24 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
string fileName = Path.GetFileName(filePath);
|
string fileName = Path.GetFileName(filePath);
|
||||||
string[] lines = File.ReadAllLines(filePath);
|
string[] lines = File.ReadAllLines(filePath);
|
||||||
|
|
||||||
bool verified = fileName switch
|
bool verified;
|
||||||
|
switch (fileName)
|
||||||
{
|
{
|
||||||
"prod.keys" or "console.keys" or "dev.keys" => VerifyKeys(lines, genericPattern),
|
case "prod.keys":
|
||||||
"title.keys" => VerifyKeys(lines, titlePattern),
|
verified = VerifyKeys(lines, genericPattern);
|
||||||
_ => throw new FormatException(
|
break;
|
||||||
$"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.")
|
case "title.keys":
|
||||||
};
|
verified = VerifyKeys(lines, titlePattern);
|
||||||
|
break;
|
||||||
|
case "console.keys":
|
||||||
|
verified = VerifyKeys(lines, genericPattern);
|
||||||
|
break;
|
||||||
|
case "dev.keys":
|
||||||
|
verified = VerifyKeys(lines, genericPattern);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!verified)
|
if (!verified)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -891,7 +891,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
result = new NestedName(name, prev);
|
result = new NestedName(name, prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.FinishWithTemplateArguments = false;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.FinishWithTemplateArguments = false;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1071,7 +1074,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.CtorDtorConversion = true;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.CtorDtorConversion = true;
|
||||||
|
}
|
||||||
|
|
||||||
return new ConversionOperatorType(type);
|
return new ConversionOperatorType(type);
|
||||||
default:
|
default:
|
||||||
@@ -1343,7 +1349,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
|
|
||||||
_position++;
|
_position++;
|
||||||
|
|
||||||
context?.CtorDtorConversion = true;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.CtorDtorConversion = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isInherited && ParseName(context) == null)
|
if (isInherited && ParseName(context) == null)
|
||||||
{
|
{
|
||||||
@@ -1363,7 +1372,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
|
|
||||||
_position++;
|
_position++;
|
||||||
|
|
||||||
context?.CtorDtorConversion = true;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.CtorDtorConversion = true;
|
||||||
|
}
|
||||||
|
|
||||||
return new CtorDtorNameType(prev, true);
|
return new CtorDtorNameType(prev, true);
|
||||||
}
|
}
|
||||||
@@ -2993,10 +3005,16 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
|
|
||||||
BaseNode result = null;
|
BaseNode result = null;
|
||||||
CvType cv = new(ParseCvQualifiers(), null);
|
CvType cv = new(ParseCvQualifiers(), null);
|
||||||
context?.Cv = cv;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.Cv = cv;
|
||||||
|
}
|
||||||
|
|
||||||
SimpleReferenceType Ref = ParseRefQualifiers();
|
SimpleReferenceType Ref = ParseRefQualifiers();
|
||||||
context?.Ref = Ref;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.Ref = Ref;
|
||||||
|
}
|
||||||
|
|
||||||
if (ConsumeIf("St"))
|
if (ConsumeIf("St"))
|
||||||
{
|
{
|
||||||
@@ -3042,7 +3060,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = new NameTypeWithTemplateArguments(result, templateArgument);
|
result = new NameTypeWithTemplateArguments(result, templateArgument);
|
||||||
context?.FinishWithTemplateArguments = true;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.FinishWithTemplateArguments = true;
|
||||||
|
}
|
||||||
|
|
||||||
_substitutionList.Add(result);
|
_substitutionList.Add(result);
|
||||||
continue;
|
continue;
|
||||||
@@ -3235,7 +3256,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.FinishWithTemplateArguments = true;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.FinishWithTemplateArguments = true;
|
||||||
|
}
|
||||||
|
|
||||||
return new NameTypeWithTemplateArguments(substitution, templateArguments);
|
return new NameTypeWithTemplateArguments(substitution, templateArguments);
|
||||||
}
|
}
|
||||||
@@ -3255,7 +3279,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.FinishWithTemplateArguments = true;
|
if (context != null)
|
||||||
|
{
|
||||||
|
context.FinishWithTemplateArguments = true;
|
||||||
|
}
|
||||||
|
|
||||||
return new NameTypeWithTemplateArguments(result, templateArguments);
|
return new NameTypeWithTemplateArguments(result, templateArguments);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Microsoft.IO;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -38,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
|
|
||||||
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr)
|
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr)
|
||||||
{
|
{
|
||||||
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data);
|
using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data);
|
||||||
|
|
||||||
BinaryReader reader = new(ms);
|
BinaryReader reader = new(ms);
|
||||||
|
|
||||||
@@ -124,8 +123,6 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||||||
}
|
}
|
||||||
|
|
||||||
ObjectIds = [];
|
ObjectIds = [];
|
||||||
|
|
||||||
MemoryStreamManager.Shared.ReleaseStream(ms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
|
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
|
||||||
|
|||||||
@@ -20,15 +20,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
_exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
|
_exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KBufferDescriptorTable Clear()
|
|
||||||
{
|
|
||||||
_sendBufferDescriptors.Clear();
|
|
||||||
_receiveBufferDescriptors.Clear();
|
|
||||||
_exchangeBufferDescriptors.Clear();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
public Result AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||||
{
|
{
|
||||||
return Add(_sendBufferDescriptors, src, dst, size, state);
|
return Add(_sendBufferDescriptors, src, dst, size, state);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
@@ -33,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
|
|
||||||
KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize);
|
KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize);
|
||||||
|
|
||||||
KernelContext.CriticalSection.Enter();
|
KernelContext.CriticalSection.Enter();
|
||||||
|
|
||||||
@@ -56,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
|
|
||||||
KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
|
KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
|
||||||
|
|
||||||
KernelContext.CriticalSection.Enter();
|
KernelContext.CriticalSection.Enter();
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
class KServerSession : KSynchronizationObject
|
class KServerSession : KSynchronizationObject
|
||||||
{
|
{
|
||||||
public readonly ObjectPool<KSessionRequest> RequestPool = new(() => new KSessionRequest());
|
|
||||||
|
|
||||||
private static readonly MemoryState[] _ipcMemoryStates =
|
private static readonly MemoryState[] _ipcMemoryStates =
|
||||||
[
|
[
|
||||||
MemoryState.IpcBuffer3,
|
MemoryState.IpcBuffer3,
|
||||||
@@ -276,8 +274,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
KernelContext.CriticalSection.Leave();
|
KernelContext.CriticalSection.Leave();
|
||||||
|
|
||||||
WakeClientThread(request, clientResult);
|
WakeClientThread(request, clientResult);
|
||||||
|
|
||||||
RequestPool.Release(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientHeader.ReceiveListType < 2 &&
|
if (clientHeader.ReceiveListType < 2 &&
|
||||||
@@ -631,8 +627,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
CloseAllHandles(clientMsg, serverHeader, clientProcess);
|
CloseAllHandles(clientMsg, serverHeader, clientProcess);
|
||||||
|
|
||||||
FinishRequest(request, clientResult);
|
FinishRequest(request, clientResult);
|
||||||
|
|
||||||
RequestPool.Release(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientHeader.ReceiveListType < 2 &&
|
if (clientHeader.ReceiveListType < 2 &&
|
||||||
@@ -872,8 +866,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
// Unmap buffers from server.
|
// Unmap buffers from server.
|
||||||
FinishRequest(request, clientResult);
|
FinishRequest(request, clientResult);
|
||||||
|
|
||||||
RequestPool.Release(request);
|
|
||||||
|
|
||||||
return serverResult;
|
return serverResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1106,8 +1098,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
|
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
|
||||||
{
|
{
|
||||||
FinishRequest(request, KernelResult.PortRemoteClosed);
|
FinishRequest(request, KernelResult.PortRemoteClosed);
|
||||||
|
|
||||||
RequestPool.Release(request);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1127,8 +1117,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
|
SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestPool.Release(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeServerThreads(KernelResult.PortRemoteClosed);
|
WakeServerThreads(KernelResult.PortRemoteClosed);
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
class KSessionRequest
|
class KSessionRequest
|
||||||
{
|
{
|
||||||
public KBufferDescriptorTable BufferDescriptorTable { get; private set; }
|
public KBufferDescriptorTable BufferDescriptorTable { get; }
|
||||||
|
|
||||||
public KThread ClientThread { get; private set; }
|
public KThread ClientThread { get; }
|
||||||
|
|
||||||
public KProcess ServerProcess { get; set; }
|
public KProcess ServerProcess { get; set; }
|
||||||
|
|
||||||
public KWritableEvent AsyncEvent { get; private set; }
|
public KWritableEvent AsyncEvent { get; }
|
||||||
|
|
||||||
public ulong CustomCmdBuffAddr { get; private set; }
|
public ulong CustomCmdBuffAddr { get; }
|
||||||
public ulong CustomCmdBuffSize { get; private set; }
|
public ulong CustomCmdBuffSize { get; }
|
||||||
|
|
||||||
public KSessionRequest Set(
|
public KSessionRequest(
|
||||||
KThread clientThread,
|
KThread clientThread,
|
||||||
ulong customCmdBuffAddr,
|
ulong customCmdBuffAddr,
|
||||||
ulong customCmdBuffSize,
|
ulong customCmdBuffSize,
|
||||||
@@ -27,9 +27,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
CustomCmdBuffSize = customCmdBuffSize;
|
CustomCmdBuffSize = customCmdBuffSize;
|
||||||
AsyncEvent = asyncEvent;
|
AsyncEvent = asyncEvent;
|
||||||
|
|
||||||
BufferDescriptorTable = BufferDescriptorTable?.Clear() ?? new KBufferDescriptorTable();
|
BufferDescriptorTable = new KBufferDescriptorTable();
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
@@ -10,12 +12,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
class KAddressArbiter
|
class KAddressArbiter
|
||||||
{
|
{
|
||||||
private const int HasListenersMask = 0x40000000;
|
private const int HasListenersMask = 0x40000000;
|
||||||
|
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
|
|
||||||
private readonly Dictionary<ulong, List<KThread>> _condVarThreads;
|
private readonly List<KThread> _condVarThreads;
|
||||||
private readonly Dictionary<ulong, List<KThread>> _arbiterThreads;
|
private readonly List<KThread> _arbiterThreads;
|
||||||
private readonly ByDynamicPriority _byDynamicPriority;
|
|
||||||
|
|
||||||
public KAddressArbiter(KernelContext context)
|
public KAddressArbiter(KernelContext context)
|
||||||
{
|
{
|
||||||
@@ -23,7 +25,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
_condVarThreads = [];
|
_condVarThreads = [];
|
||||||
_arbiterThreads = [];
|
_arbiterThreads = [];
|
||||||
_byDynamicPriority = new ByDynamicPriority();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
||||||
@@ -139,23 +140,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
currentThread.MutexAddress = mutexAddress;
|
currentThread.MutexAddress = mutexAddress;
|
||||||
currentThread.ThreadHandleForUserMutex = threadHandle;
|
currentThread.ThreadHandleForUserMutex = threadHandle;
|
||||||
|
currentThread.CondVarAddress = condVarAddress;
|
||||||
|
|
||||||
if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads))
|
_condVarThreads.Add(currentThread);
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (threads.Count > 0)
|
|
||||||
{
|
|
||||||
i = threads.BinarySearch(currentThread, _byDynamicPriority);
|
|
||||||
if (i < 0) i = ~i;
|
|
||||||
}
|
|
||||||
|
|
||||||
threads.Insert(i, currentThread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_condVarThreads.Add(condVarAddress, [currentThread]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout != 0)
|
if (timeout != 0)
|
||||||
{
|
{
|
||||||
@@ -178,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
currentThread.MutexOwner?.RemoveMutexWaiter(currentThread);
|
currentThread.MutexOwner?.RemoveMutexWaiter(currentThread);
|
||||||
|
|
||||||
_condVarThreads[condVarAddress].Remove(currentThread);
|
_condVarThreads.Remove(currentThread);
|
||||||
|
|
||||||
_context.CriticalSection.Leave();
|
_context.CriticalSection.Leave();
|
||||||
|
|
||||||
@@ -213,14 +200,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
_context.CriticalSection.Enter();
|
_context.CriticalSection.Enter();
|
||||||
|
|
||||||
int validThreads = 0;
|
static bool SignalProcessWideKeyPredicate(KThread thread, ulong address)
|
||||||
_condVarThreads.TryGetValue(address, out List<KThread> threads);
|
|
||||||
|
|
||||||
if (threads is not null && threads.Count > 0)
|
|
||||||
{
|
{
|
||||||
validThreads = WakeThreads(threads, count, TryAcquireMutex);
|
return thread.CondVarAddress == address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address);
|
||||||
|
|
||||||
if (validThreads == 0)
|
if (validThreads == 0)
|
||||||
{
|
{
|
||||||
KernelTransfer.KernelToUser(address, 0);
|
KernelTransfer.KernelToUser(address, 0);
|
||||||
@@ -330,22 +316,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
currentThread.MutexAddress = address;
|
currentThread.MutexAddress = address;
|
||||||
currentThread.WaitingInArbitration = true;
|
currentThread.WaitingInArbitration = true;
|
||||||
|
|
||||||
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
_arbiterThreads.Add(currentThread);
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (threads.Count > 0)
|
|
||||||
{
|
|
||||||
i = threads.BinarySearch(currentThread, _byDynamicPriority);
|
|
||||||
if (i < 0) i = ~i;
|
|
||||||
}
|
|
||||||
|
|
||||||
threads.Insert(i, currentThread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_arbiterThreads.Add(address, [currentThread]);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
@@ -365,7 +336,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (currentThread.WaitingInArbitration)
|
if (currentThread.WaitingInArbitration)
|
||||||
{
|
{
|
||||||
_arbiterThreads[address].Remove(currentThread);
|
_arbiterThreads.Remove(currentThread);
|
||||||
|
|
||||||
currentThread.WaitingInArbitration = false;
|
currentThread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
@@ -422,22 +393,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
currentThread.MutexAddress = address;
|
currentThread.MutexAddress = address;
|
||||||
currentThread.WaitingInArbitration = true;
|
currentThread.WaitingInArbitration = true;
|
||||||
|
|
||||||
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
_arbiterThreads.Add(currentThread);
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (threads.Count > 0)
|
|
||||||
{
|
|
||||||
i = threads.BinarySearch(currentThread, _byDynamicPriority);
|
|
||||||
if (i < 0) i = ~i;
|
|
||||||
}
|
|
||||||
|
|
||||||
threads.Insert(i, currentThread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_arbiterThreads.Add(address, [currentThread]);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
@@ -457,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (currentThread.WaitingInArbitration)
|
if (currentThread.WaitingInArbitration)
|
||||||
{
|
{
|
||||||
_arbiterThreads[address].Remove(currentThread);
|
_arbiterThreads.Remove(currentThread);
|
||||||
|
|
||||||
currentThread.WaitingInArbitration = false;
|
currentThread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
@@ -531,12 +487,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// or negative. It is incremented if there are no threads waiting.
|
// or negative. It is incremented if there are no threads waiting.
|
||||||
int waitingCount = 0;
|
int waitingCount = 0;
|
||||||
|
|
||||||
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
foreach (KThread thread in _arbiterThreads)
|
||||||
{
|
{
|
||||||
waitingCount = threads.Count;
|
if (thread.MutexAddress == address &&
|
||||||
|
++waitingCount >= count)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (waitingCount > 0)
|
if (waitingCount > 0)
|
||||||
{
|
{
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
@@ -602,38 +561,55 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
thread.WaitingInArbitration = false;
|
thread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_arbiterThreads.TryGetValue(address, out List<KThread> threads);
|
static bool ArbiterThreadPredecate(KThread thread, ulong address)
|
||||||
|
|
||||||
if (threads is not null && threads.Count > 0)
|
|
||||||
{
|
{
|
||||||
WakeThreads(threads, count, RemoveArbiterThread);
|
return thread.MutexAddress == address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int WakeThreads(
|
private static int WakeThreads(
|
||||||
List<KThread> threads,
|
List<KThread> threads,
|
||||||
int count,
|
int count,
|
||||||
Action<KThread> removeCallback)
|
Action<KThread> removeCallback,
|
||||||
|
Func<KThread, ulong, bool> predicate,
|
||||||
|
ulong address = 0)
|
||||||
{
|
{
|
||||||
int validCount = count > 0 ? Math.Min(count, threads.Count) : threads.Count;
|
KThread[] candidates = _threadArrayPool.Allocate();
|
||||||
|
if (candidates.Length < threads.Count)
|
||||||
for (int i = 0; i < validCount; i++)
|
|
||||||
{
|
{
|
||||||
KThread thread = threads[i];
|
Array.Resize(ref candidates, threads.Count);
|
||||||
removeCallback(thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
threads.RemoveRange(0, validCount);
|
int validCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < threads.Count; i++)
|
||||||
|
{
|
||||||
|
if (predicate(threads[i], address))
|
||||||
|
{
|
||||||
|
candidates[validCount++] = threads[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<KThread> candidatesSpan = candidates.AsSpan(..validCount);
|
||||||
|
|
||||||
|
candidatesSpan.Sort((x, y) => (x.DynamicPriority.CompareTo(y.DynamicPriority)));
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
candidatesSpan = candidatesSpan[..Math.Min(count, candidatesSpan.Length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KThread thread in candidatesSpan)
|
||||||
|
{
|
||||||
|
removeCallback(thread);
|
||||||
|
threads.Remove(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
_threadArrayPool.Release(candidates);
|
||||||
|
|
||||||
return validCount;
|
return validCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ByDynamicPriority : IComparer<KThread>
|
|
||||||
{
|
|
||||||
public int Compare(KThread x, KThread y)
|
|
||||||
{
|
|
||||||
return x!.DynamicPriority.CompareTo(y!.DynamicPriority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (previousThread != nextThread)
|
if (previousThread != nextThread)
|
||||||
{
|
{
|
||||||
previousThread?.LastScheduledTime = PerformanceCounter.ElapsedTicks;
|
if (previousThread != null)
|
||||||
|
{
|
||||||
|
previousThread.LastScheduledTime = PerformanceCounter.ElapsedTicks;
|
||||||
|
}
|
||||||
|
|
||||||
_state.SelectedThread = nextThread;
|
_state.SelectedThread = nextThread;
|
||||||
_state.NeedsScheduling = true;
|
_state.NeedsScheduling = true;
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public KSynchronizationObject SignaledObj { get; set; }
|
public KSynchronizationObject SignaledObj { get; set; }
|
||||||
|
|
||||||
|
public ulong CondVarAddress { get; set; }
|
||||||
|
|
||||||
private ulong _entrypoint;
|
private ulong _entrypoint;
|
||||||
private ThreadStart _customThreadStart;
|
private ThreadStart _customThreadStart;
|
||||||
private bool _forcedUnschedulable;
|
private bool _forcedUnschedulable;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter<AccountState>))]
|
[JsonConverter(typeof(TypedStringEnumConverter<AccountState>))]
|
||||||
public enum AccountState
|
public enum AccountState
|
||||||
{
|
{
|
||||||
Closed,
|
Closed,
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
return ResultCode.InvalidParameters;
|
return ResultCode.InvalidParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Device.UIHandler.TakeScreenshot();
|
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { albumReportOption });
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Horizon.Common;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Ectx
|
|
||||||
{
|
|
||||||
class IContextRegistrar : DisposableIpcService
|
|
||||||
{
|
|
||||||
public IContextRegistrar(ServiceCtx context) { }
|
|
||||||
|
|
||||||
[CommandCmif(0)] // 11.0.0+
|
|
||||||
// Complete(nn::Result result, buffer<bytes, 5> raw_context) -> (i32 context_descriptor)
|
|
||||||
public ResultCode Complete(ServiceCtx context)
|
|
||||||
{
|
|
||||||
Result result = new(context.RequestData.ReadInt32());
|
|
||||||
ulong rawContextPosition = context.Request.SendBuff[0].Position;
|
|
||||||
ulong rawContextSize = context.Request.SendBuff[0].Size;
|
|
||||||
|
|
||||||
byte[] rawContext = new byte[rawContextSize];
|
|
||||||
|
|
||||||
context.Memory.Read(rawContextPosition, rawContext);
|
|
||||||
|
|
||||||
context.ResponseData.Write(0); // TODO: return context_descriptor
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceEctx, $"Result: {result}, rawContext: {Convert.ToHexString(rawContext)}" );
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,5 @@ namespace Ryujinx.HLE.HOS.Services.Ectx
|
|||||||
class IWriterForApplication : IpcService
|
class IWriterForApplication : IpcService
|
||||||
{
|
{
|
||||||
public IWriterForApplication(ServiceCtx context) { }
|
public IWriterForApplication(ServiceCtx context) { }
|
||||||
|
|
||||||
[CommandCmif(0)]
|
|
||||||
// CreateContextRegistrar() -> object<nn::err::context::IContextRegistrar>
|
|
||||||
public ResultCode CreateContextRegistrar(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MakeObject(context, new IContextRegistrar(context));
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,6 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
private int _selfId;
|
private int _selfId;
|
||||||
private bool _isDomain;
|
private bool _isDomain;
|
||||||
|
|
||||||
// cache array so we don't recreate it all the time
|
|
||||||
private object[] _parameters = [null];
|
|
||||||
|
|
||||||
public IpcService(ServerBase server = null, bool registerTipc = false)
|
public IpcService(ServerBase server = null, bool registerTipc = false)
|
||||||
{
|
{
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
@@ -149,9 +146,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
|
Logger.Trace?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
|
||||||
|
|
||||||
_parameters[0] = context;
|
result = (ResultCode)processRequest.Invoke(service, [context]);
|
||||||
|
|
||||||
result = (ResultCode)processRequest.Invoke(service, _parameters);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -201,9 +196,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.KernelIpc, $"{GetType().Name}: {processRequest.Name}");
|
Logger.Debug?.Print(LogClass.KernelIpc, $"{GetType().Name}: {processRequest.Name}");
|
||||||
|
|
||||||
_parameters[0] = context;
|
result = (ResultCode)processRequest.Invoke(this, [context]);
|
||||||
|
|
||||||
result = (ResultCode)processRequest.Invoke(this, _parameters);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,10 +60,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
// TODO: This should call set:sys::GetDebugModeFlag
|
// TODO: This should call set:sys::GetDebugModeFlag
|
||||||
private readonly bool _debugModeEnabled = false;
|
private readonly bool _debugModeEnabled = false;
|
||||||
|
|
||||||
private byte[] _ioctl2Buffer = [];
|
|
||||||
private byte[] _ioctlArgumentBuffer = [];
|
|
||||||
private byte[] _ioctl3Buffer = [];
|
|
||||||
|
|
||||||
public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer)
|
public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer)
|
||||||
{
|
{
|
||||||
_owner = 0;
|
_owner = 0;
|
||||||
@@ -132,38 +128,27 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments))
|
if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments))
|
||||||
{
|
{
|
||||||
if (_ioctlArgumentBuffer.Length < (int)inputDataSize)
|
arguments = new byte[inputDataSize];
|
||||||
{
|
|
||||||
Array.Resize(ref _ioctlArgumentBuffer, (int)inputDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments = _ioctlArgumentBuffer.AsSpan(0, (int)inputDataSize);
|
|
||||||
|
|
||||||
context.Memory.Read(inputDataPosition, arguments);
|
context.Memory.Read(inputDataPosition, arguments);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arguments = arguments.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (isWrite)
|
else if (isWrite)
|
||||||
{
|
{
|
||||||
if (_ioctlArgumentBuffer.Length < (int)outputDataSize)
|
byte[] outputData = new byte[outputDataSize];
|
||||||
{
|
|
||||||
Array.Resize(ref _ioctlArgumentBuffer, (int)outputDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments = _ioctlArgumentBuffer.AsSpan(0, (int)outputDataSize);
|
arguments = new Span<byte>(outputData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments))
|
byte[] temp = new byte[inputDataSize];
|
||||||
{
|
|
||||||
if (_ioctlArgumentBuffer.Length < (int)inputDataSize)
|
|
||||||
{
|
|
||||||
Array.Resize(ref _ioctlArgumentBuffer, (int)inputDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments = _ioctlArgumentBuffer.AsSpan(0, (int)inputDataSize);
|
context.Memory.Read(inputDataPosition, temp);
|
||||||
|
|
||||||
context.Memory.Read(inputDataPosition, arguments);
|
arguments = new Span<byte>(temp);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NvResult.Success;
|
return NvResult.Success;
|
||||||
@@ -285,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||||
{
|
{
|
||||||
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments);
|
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,15 +474,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||||
|
|
||||||
|
byte[] inlineInBuffer = null;
|
||||||
|
|
||||||
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBufferSpan))
|
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBufferSpan))
|
||||||
{
|
{
|
||||||
if (_ioctl2Buffer.Length < (int)inlineInBufferSize)
|
inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize);
|
||||||
{
|
inlineInBufferSpan = inlineInBuffer;
|
||||||
Array.Resize(ref _ioctl2Buffer, (int)inlineInBufferSize);
|
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]);
|
||||||
}
|
|
||||||
|
|
||||||
inlineInBufferSpan = _ioctl2Buffer.AsSpan(0, (int)inlineInBufferSize);
|
|
||||||
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
@@ -506,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
{
|
{
|
||||||
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan);
|
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]);
|
||||||
|
|
||||||
if (internalResult == NvInternalResult.NotImplemented)
|
if (internalResult == NvInternalResult.NotImplemented)
|
||||||
{
|
{
|
||||||
@@ -517,10 +500,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||||
{
|
{
|
||||||
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments);
|
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inlineInBuffer is not null)
|
||||||
|
{
|
||||||
|
_byteArrayPool.Return(inlineInBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ResponseData.Write((uint)errorCode);
|
context.ResponseData.Write((uint)errorCode);
|
||||||
@@ -543,15 +531,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||||
|
|
||||||
|
byte[] inlineOutBuffer = null;
|
||||||
|
|
||||||
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBufferSpan))
|
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBufferSpan))
|
||||||
{
|
{
|
||||||
if (_ioctl3Buffer.Length < (int)inlineOutBufferSize)
|
inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize);
|
||||||
{
|
inlineOutBufferSpan = inlineOutBuffer;
|
||||||
Array.Resize(ref _ioctl3Buffer, (int)inlineOutBufferSize);
|
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
|
||||||
}
|
|
||||||
|
|
||||||
inlineOutBufferSpan = _ioctl3Buffer.AsSpan(0, (int)inlineOutBufferSize);
|
|
||||||
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
@@ -560,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
{
|
{
|
||||||
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan);
|
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
|
||||||
|
|
||||||
if (internalResult == NvInternalResult.NotImplemented)
|
if (internalResult == NvInternalResult.NotImplemented)
|
||||||
{
|
{
|
||||||
@@ -571,11 +557,16 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||||
{
|
{
|
||||||
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments);
|
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||||
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan);
|
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inlineOutBuffer is not null)
|
||||||
|
{
|
||||||
|
_byteArrayPool.Return(inlineOutBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ResponseData.Write((uint)errorCode);
|
context.ResponseData.Write((uint)errorCode);
|
||||||
|
|||||||
@@ -454,9 +454,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
response.RawData = _responseDataStream.ToArray();
|
response.RawData = _responseDataStream.ToArray();
|
||||||
|
|
||||||
RecyclableMemoryStream responseStream = response.GetStreamTipc();
|
using RecyclableMemoryStream responseStream = response.GetStreamTipc();
|
||||||
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
|
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
|
||||||
MemoryStreamManager.Shared.ReleaseStream(responseStream);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -465,9 +464,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
if (!isTipcCommunication)
|
if (!isTipcCommunication)
|
||||||
{
|
{
|
||||||
RecyclableMemoryStream responseStream = response.GetStream((long)_selfThread.TlsAddress, recvListAddr | ((ulong)PointerBufferSize << 48));
|
using RecyclableMemoryStream responseStream = response.GetStream((long)_selfThread.TlsAddress, recvListAddr | ((ulong)PointerBufferSize << 48));
|
||||||
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
|
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
|
||||||
MemoryStreamManager.Shared.ReleaseStream(responseStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldReply;
|
return shouldReply;
|
||||||
|
|||||||
@@ -1169,7 +1169,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
|
|
||||||
public override void DestroyAtExit()
|
public override void DestroyAtExit()
|
||||||
{
|
{
|
||||||
_context?.Dispose();
|
if (_context != null) {
|
||||||
|
_context.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,10 +68,5 @@ namespace Ryujinx.HLE.UI
|
|||||||
/// Displays the player select dialog and returns the selected profile.
|
/// Displays the player select dialog and returns the selected profile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
UserProfile ShowPlayerSelectDialog();
|
UserProfile ShowPlayerSelectDialog();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Takes a screenshot from the current renderer and saves it in the screenshots folder.
|
|
||||||
/// </summary>
|
|
||||||
void TakeScreenshot();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|||||||
|
|
||||||
private MultiWaitHolderBase _signaledHolder;
|
private MultiWaitHolderBase _signaledHolder;
|
||||||
|
|
||||||
ObjectPool<int[]> _objectHandlePool = new(() => new int[64]);
|
|
||||||
ObjectPool<MultiWaitHolderBase[]> _objectPool = new(() => new MultiWaitHolderBase[64]);
|
|
||||||
|
|
||||||
public long CurrentTime { get; private set; }
|
public long CurrentTime { get; private set; }
|
||||||
|
|
||||||
public IEnumerable<MultiWaitHolderBase> MultiWaits => _multiWaits;
|
public IEnumerable<MultiWaitHolderBase> MultiWaits => _multiWaits;
|
||||||
@@ -79,15 +76,11 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|||||||
|
|
||||||
private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout)
|
private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout)
|
||||||
{
|
{
|
||||||
int[] objectHandles = _objectHandlePool.Allocate();
|
Span<int> objectHandles = new int[64];
|
||||||
Span<int> objectHandlesSpan = objectHandles;
|
|
||||||
objectHandlesSpan.Clear();
|
|
||||||
|
|
||||||
MultiWaitHolderBase[] objects = _objectPool.Allocate();
|
Span<MultiWaitHolderBase> objects = new MultiWaitHolderBase[64];
|
||||||
Span<MultiWaitHolderBase> objectsSpan = objects;
|
|
||||||
objectsSpan.Clear();
|
|
||||||
|
|
||||||
int count = FillObjectsArray(objectHandlesSpan, objectsSpan);
|
int count = FillObjectsArray(objectHandles, objects);
|
||||||
|
|
||||||
long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000;
|
long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000;
|
||||||
|
|
||||||
@@ -105,7 +98,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = WaitSynchronization(objectHandlesSpan[..count], minTimeout);
|
index = WaitSynchronization(objectHandles[..count], minTimeout);
|
||||||
|
|
||||||
DebugUtil.Assert(index != WaitInvalid);
|
DebugUtil.Assert(index != WaitInvalid);
|
||||||
}
|
}
|
||||||
@@ -123,18 +116,12 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|||||||
{
|
{
|
||||||
_signaledHolder = minTimeoutObject;
|
_signaledHolder = minTimeoutObject;
|
||||||
|
|
||||||
_objectHandlePool.Release(objectHandles);
|
|
||||||
_objectPool.Release(objects);
|
|
||||||
|
|
||||||
return _signaledHolder;
|
return _signaledHolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_objectHandlePool.Release(objectHandles);
|
|
||||||
_objectPool.Release(objects);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,9 +131,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|||||||
{
|
{
|
||||||
if (_signaledHolder != null)
|
if (_signaledHolder != null)
|
||||||
{
|
{
|
||||||
_objectHandlePool.Release(objectHandles);
|
|
||||||
_objectPool.Release(objects);
|
|
||||||
|
|
||||||
return _signaledHolder;
|
return _signaledHolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,10 +139,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|||||||
default:
|
default:
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_signaledHolder = objectsSpan[index];
|
_signaledHolder = objects[index];
|
||||||
|
|
||||||
_objectHandlePool.Release(objectHandles);
|
|
||||||
_objectPool.Release(objects);
|
|
||||||
|
|
||||||
return _signaledHolder;
|
return _signaledHolder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Ryujinx.Input.SDL3
|
|||||||
|
|
||||||
private StandardControllerInputConfig _configuration;
|
private StandardControllerInputConfig _configuration;
|
||||||
|
|
||||||
private readonly SDL_GamepadButton[] _buttonsDriverMapping =
|
private static readonly SDL_GamepadButton[] _buttonsDriverMapping =
|
||||||
[
|
[
|
||||||
// Unbound, ignored.
|
// Unbound, ignored.
|
||||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||||
@@ -88,20 +88,6 @@ namespace Ryujinx.Input.SDL3
|
|||||||
Features = GetFeaturesFlag();
|
Features = GetFeaturesFlag();
|
||||||
_triggerThreshold = 0.0f;
|
_triggerThreshold = 0.0f;
|
||||||
|
|
||||||
// Face button mapping
|
|
||||||
SDL_GamepadButton[] faceButtons = _buttonsDriverMapping[1..5];
|
|
||||||
foreach (SDL_GamepadButton btn in faceButtons) {
|
|
||||||
int mapId = SDL_GetGamepadButtonLabel(_gamepadHandle, btn) switch {
|
|
||||||
SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_A or SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_CROSS => 1,
|
|
||||||
SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_B or SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_CIRCLE => 2,
|
|
||||||
SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_X or SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_SQUARE => 3,
|
|
||||||
SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_Y or SDL_GamepadButtonLabel.SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE => 4,
|
|
||||||
_ => -1
|
|
||||||
};
|
|
||||||
if (mapId == -1) { continue; }
|
|
||||||
_buttonsDriverMapping[mapId] = btn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable motion tracking
|
// Enable motion tracking
|
||||||
if ((Features & GamepadFeaturesFlag.Motion) != 0)
|
if ((Features & GamepadFeaturesFlag.Motion) != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace Ryujinx.Input.SDL3
|
|||||||
|
|
||||||
string strGuid = new(guidBytes);
|
string strGuid = new(guidBytes);
|
||||||
|
|
||||||
return $"{strGuid[4..6]}{strGuid[6..8]}{strGuid[2..4]}{strGuid[0..2]}-{strGuid[10..12]}{strGuid[8..10]}-{strGuid[12..16]}-{strGuid[16..20]}-{strGuid[20..32]}";
|
return $"{strGuid[0..8]}-{strGuid[8..12]}-{strGuid[12..16]}-{strGuid[16..20]}-{strGuid[20..32]}";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -533,6 +533,8 @@ namespace Ryujinx.Input.HLE
|
|||||||
hidKeyboard.Modifier |= value << entry.Target;
|
hidKeyboard.Modifier |= value << entry.Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<bool>.Shared.Return(keyboardState.KeysState);
|
||||||
|
|
||||||
return hidKeyboard;
|
return hidKeyboard;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
public class NpadManager : IDisposable
|
public class NpadManager : IDisposable
|
||||||
{
|
{
|
||||||
|
private static readonly ObjectPool<List<SixAxisInput>> _hleMotionStatesPool = new (() => new List<SixAxisInput>(NpadDevices.MaxControllers));
|
||||||
private readonly CemuHookClient _cemuHookClient;
|
private readonly CemuHookClient _cemuHookClient;
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
@@ -40,9 +41,6 @@ namespace Ryujinx.Input.HLE
|
|||||||
private bool _enableMouse;
|
private bool _enableMouse;
|
||||||
private Switch _device;
|
private Switch _device;
|
||||||
|
|
||||||
private readonly List<GamepadInput> _hleInputStates = [];
|
|
||||||
private readonly List<SixAxisInput> _hleMotionStates = new(NpadDevices.MaxControllers);
|
|
||||||
|
|
||||||
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver)
|
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver)
|
||||||
{
|
{
|
||||||
_controllers = new NpadController[MaxControllers];
|
_controllers = new NpadController[MaxControllers];
|
||||||
@@ -219,8 +217,8 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_hleInputStates.Clear();
|
List<GamepadInput> hleInputStates = [];
|
||||||
_hleMotionStates.Clear();
|
List<SixAxisInput> hleMotionStates = _hleMotionStatesPool.Allocate();
|
||||||
|
|
||||||
KeyboardInput? hleKeyboardInput = null;
|
KeyboardInput? hleKeyboardInput = null;
|
||||||
|
|
||||||
@@ -262,14 +260,14 @@ namespace Ryujinx.Input.HLE
|
|||||||
inputState.PlayerId = playerIndex;
|
inputState.PlayerId = playerIndex;
|
||||||
motionState.Item1.PlayerId = playerIndex;
|
motionState.Item1.PlayerId = playerIndex;
|
||||||
|
|
||||||
_hleInputStates.Add(inputState);
|
hleInputStates.Add(inputState);
|
||||||
_hleMotionStates.Add(motionState.Item1);
|
hleMotionStates.Add(motionState.Item1);
|
||||||
|
|
||||||
if (isJoyconPair && !motionState.Item2.Equals(default))
|
if (isJoyconPair && !motionState.Item2.Equals(default))
|
||||||
{
|
{
|
||||||
motionState.Item2.PlayerId = playerIndex;
|
motionState.Item2.PlayerId = playerIndex;
|
||||||
|
|
||||||
_hleMotionStates.Add(motionState.Item2);
|
hleMotionStates.Add(motionState.Item2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,8 +276,8 @@ namespace Ryujinx.Input.HLE
|
|||||||
hleKeyboardInput = NpadController.GetHLEKeyboardInput(_keyboardDriver);
|
hleKeyboardInput = NpadController.GetHLEKeyboardInput(_keyboardDriver);
|
||||||
}
|
}
|
||||||
|
|
||||||
_device.Hid.Npads.Update(_hleInputStates);
|
_device.Hid.Npads.Update(hleInputStates);
|
||||||
_device.Hid.Npads.UpdateSixAxis(_hleMotionStates);
|
_device.Hid.Npads.UpdateSixAxis(hleMotionStates);
|
||||||
|
|
||||||
if (hleKeyboardInput.HasValue)
|
if (hleKeyboardInput.HasValue)
|
||||||
{
|
{
|
||||||
@@ -330,7 +328,9 @@ namespace Ryujinx.Input.HLE
|
|||||||
_device.Hid.Mouse.Update(0, 0);
|
_device.Hid.Mouse.Update(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_device.TamperMachine.UpdateInput(_hleInputStates);
|
_device.TamperMachine.UpdateInput(hleInputStates);
|
||||||
|
|
||||||
|
_hleMotionStatesPool.Release(hleMotionStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ namespace Ryujinx.Input
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IKeyboard : IGamepad
|
public interface IKeyboard : IGamepad
|
||||||
{
|
{
|
||||||
private static bool[] _keyState;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if a given key is pressed on the keyboard.
|
/// Check if a given key is pressed on the keyboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -31,17 +29,15 @@ namespace Ryujinx.Input
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
|
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
|
||||||
{
|
{
|
||||||
if (_keyState is null)
|
|
||||||
{
|
bool[] keysState = ArrayPool<bool>.Shared.Rent((int)Key.Count);
|
||||||
_keyState = new bool[(int)Key.Count];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Key key = 0; key < Key.Count; key++)
|
for (Key key = 0; key < Key.Count; key++)
|
||||||
{
|
{
|
||||||
_keyState[(int)key] = keyboard.IsPressed(key);
|
keysState[(int)key] = keyboard.IsPressed(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new KeyboardStateSnapshot(_keyState);
|
return new KeyboardStateSnapshot(keysState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Range of memory that can be split in two.
|
/// Range of memory that can be split in two.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface INonOverlappingRange<T> : IRangeListRange<T> where T : class, IRangeListRange<T>
|
public interface INonOverlappingRange : IRange
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Split this region into two, around the specified address.
|
/// Split this region into two, around the specified address.
|
||||||
@@ -11,6 +11,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="splitAddress">Address to split the region around</param>
|
/// <param name="splitAddress">Address to split the region around</param>
|
||||||
/// <returns>The second part of the split region, with start address at the given split.</returns>
|
/// <returns>The second part of the split region, with start address at the given split.</returns>
|
||||||
public INonOverlappingRange<T> Split(ulong splitAddress);
|
public INonOverlappingRange Split(ulong splitAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// Check if this range overlaps with another.
|
/// Check if this range overlaps with another.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Base address</param>
|
/// <param name="address">Base address</param>
|
||||||
/// <param name="endAddress">EndAddress of the range</param>
|
/// <param name="size">Size of the range</param>
|
||||||
/// <returns>True if overlapping, false otherwise</returns>
|
/// <returns>True if overlapping, false otherwise</returns>
|
||||||
bool OverlapsWith(ulong address, ulong endAddress);
|
bool OverlapsWith(ulong address, ulong size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// A range list that assumes ranges are non-overlapping, with list items that can be split in two to avoid overlaps.
|
/// A range list that assumes ranges are non-overlapping, with list items that can be split in two to avoid overlaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the range.</typeparam>
|
/// <typeparam name="T">Type of the range.</typeparam>
|
||||||
public class NonOverlappingRangeList<T> : RangeListBase<T> where T : class, INonOverlappingRange<T>
|
public unsafe class NonOverlappingRangeList<T> : RangeListBase<T> where T : class, INonOverlappingRange
|
||||||
{
|
{
|
||||||
public readonly ReaderWriterLockSlim Lock = new();
|
public readonly ReaderWriterLockSlim Lock = new();
|
||||||
|
|
||||||
@@ -32,8 +32,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="item">The item to be added</param>
|
/// <param name="item">The item to be added</param>
|
||||||
public override void Add(T item)
|
public override void Add(T item)
|
||||||
{
|
{
|
||||||
Debug.Assert(item.Address != item.EndAddress);
|
|
||||||
|
|
||||||
int index = BinarySearch(item.Address);
|
int index = BinarySearch(item.Address);
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@@ -41,9 +39,76 @@ namespace Ryujinx.Memory.Range
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangeItem<T> rangeItem = _rangeItemPool.Allocate().Set(item);
|
||||||
|
|
||||||
|
Insert(index, rangeItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an item's end address on the list. Address must be the same.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to be updated</param>
|
||||||
|
/// <returns>True if the item was located and updated, false otherwise</returns>
|
||||||
|
protected override bool Update(T item)
|
||||||
|
{
|
||||||
|
int index = BinarySearch(item.Address);
|
||||||
|
|
||||||
|
if (index >= 0 && Items[index].Value.Equals(item))
|
||||||
|
{
|
||||||
|
RangeItem<T> rangeItem = new(item) { Previous = Items[index].Previous, Next = Items[index].Next };
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Items[index - 1].Next = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < Count - 1)
|
||||||
|
{
|
||||||
|
Items[index + 1].Previous = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an item's end address on the list. Address must be the same.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The RangeItem to be updated</param>
|
||||||
|
/// <returns>True if the item was located and updated, false otherwise</returns>
|
||||||
|
protected override bool Update(RangeItem<T> item)
|
||||||
|
{
|
||||||
|
int index = BinarySearch(item.Address);
|
||||||
|
|
||||||
|
RangeItem<T> rangeItem = new(item.Value) { Previous = item.Previous, Next = item.Next };
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Items[index - 1].Next = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < Count - 1)
|
||||||
|
{
|
||||||
|
Items[index + 1].Previous = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void Insert(int index, RangeItem<T> item)
|
||||||
|
{
|
||||||
|
Debug.Assert(item.Address != item.EndAddress);
|
||||||
|
|
||||||
if (Count + 1 > Items.Length)
|
if (Count + 1 > Items.Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref Items, (int)(Items.Length * 1.5));
|
Array.Resize(ref Items, Items.Length + BackingGrowthSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= Count)
|
if (index >= Count)
|
||||||
@@ -80,6 +145,8 @@ namespace Ryujinx.Memory.Range
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void RemoveAt(int index)
|
private void RemoveAt(int index)
|
||||||
{
|
{
|
||||||
|
_rangeItemPool.Release(Items[index]);
|
||||||
|
|
||||||
if (index < Count - 1)
|
if (index < Count - 1)
|
||||||
{
|
{
|
||||||
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
||||||
@@ -106,7 +173,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
int index = BinarySearch(item.Address);
|
int index = BinarySearch(item.Address);
|
||||||
|
|
||||||
if (index >= 0 && Items[index] == item)
|
if (index >= 0 && Items[index].Value.Equals(item))
|
||||||
{
|
{
|
||||||
RemoveAt(index);
|
RemoveAt(index);
|
||||||
|
|
||||||
@@ -121,7 +188,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startItem">The first item in the range of items to be removed</param>
|
/// <param name="startItem">The first item in the range of items to be removed</param>
|
||||||
/// <param name="endItem">The last item in the range of items to be removed</param>
|
/// <param name="endItem">The last item in the range of items to be removed</param>
|
||||||
public override void RemoveRange(T startItem, T endItem)
|
public override void RemoveRange(RangeItem<T> startItem, RangeItem<T> endItem)
|
||||||
{
|
{
|
||||||
if (startItem is null)
|
if (startItem is null)
|
||||||
{
|
{
|
||||||
@@ -130,7 +197,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
|
|
||||||
if (startItem == endItem)
|
if (startItem == endItem)
|
||||||
{
|
{
|
||||||
Remove(startItem);
|
Remove(startItem.Value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,45 +229,42 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size of the range</param>
|
/// <param name="size">Size of the range</param>
|
||||||
public void RemoveRange(ulong address, ulong size)
|
public void RemoveRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
(int startIndex, int endIndex) = BinarySearchEdges(address, address + size);
|
int startIndex = BinarySearchLeftEdge(address, address + size);
|
||||||
|
|
||||||
if (startIndex < 0)
|
if (startIndex < 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startIndex == endIndex - 1)
|
int endIndex = startIndex;
|
||||||
|
|
||||||
|
while (Items[endIndex] is not null && Items[endIndex].Address < address + size)
|
||||||
{
|
{
|
||||||
RemoveAt(startIndex);
|
if (endIndex == Count - 1)
|
||||||
return;
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
endIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveRangeInternal(startIndex, endIndex);
|
if (endIndex < Count - 1)
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a range of items from the item list
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">Start index of the range</param>
|
|
||||||
/// <param name="endIndex">End index of the range (exclusive)</param>
|
|
||||||
private void RemoveRangeInternal(int index, int endIndex)
|
|
||||||
{
|
|
||||||
if (endIndex < Count)
|
|
||||||
{
|
{
|
||||||
Items[endIndex].Previous = index > 0 ? Items[index - 1] : null;
|
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > 0)
|
if (startIndex > 0)
|
||||||
{
|
{
|
||||||
Items[index - 1].Next = endIndex < Count ? Items[endIndex] : null;
|
Items[startIndex - 1].Next = endIndex < Count - 1 ? Items[endIndex + 1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endIndex < Count)
|
|
||||||
|
if (endIndex < Count - 1)
|
||||||
{
|
{
|
||||||
Array.Copy(Items, endIndex, Items, index, Count - endIndex);
|
Array.Copy(Items, endIndex + 1, Items, startIndex, Count - endIndex - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Count -= endIndex - index;
|
Count -= endIndex - startIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -232,8 +296,8 @@ namespace Ryujinx.Memory.Range
|
|||||||
// So we need to return both the split 0-1 and 1-2 ranges.
|
// So we need to return both the split 0-1 and 1-2 ranges.
|
||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
(T first, T last) = FindOverlapsAsNodes(address, size);
|
(RangeItem<T> first, RangeItem<T> last) = FindOverlapsAsNodes(address, size);
|
||||||
list = [];
|
list = new List<T>();
|
||||||
|
|
||||||
if (first is null)
|
if (first is null)
|
||||||
{
|
{
|
||||||
@@ -247,41 +311,42 @@ namespace Ryujinx.Memory.Range
|
|||||||
ulong lastAddress = address;
|
ulong lastAddress = address;
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
T current = first;
|
RangeItem<T> current = first;
|
||||||
while (last is not null && current is not null && current.Address < endAddress)
|
while (last is not null && current is not null && current.Address < endAddress)
|
||||||
{
|
{
|
||||||
if (first == last && current.Address == address && current.Size == size)
|
T region = current.Value;
|
||||||
|
if (first == last && region.Address == address && region.Size == size)
|
||||||
{
|
{
|
||||||
// Exact match, no splitting required.
|
// Exact match, no splitting required.
|
||||||
list.Add(current);
|
list.Add(region);
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastAddress < current.Address)
|
if (lastAddress < region.Address)
|
||||||
{
|
{
|
||||||
// There is a gap between this region and the last. We need to fill it.
|
// There is a gap between this region and the last. We need to fill it.
|
||||||
T fillRegion = factory(lastAddress, current.Address - lastAddress);
|
T fillRegion = factory(lastAddress, region.Address - lastAddress);
|
||||||
list.Add(fillRegion);
|
list.Add(fillRegion);
|
||||||
Add(fillRegion);
|
Add(fillRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.Address < address)
|
if (region.Address < address)
|
||||||
{
|
{
|
||||||
// Split the region around our base address and take the high half.
|
// Split the region around our base address and take the high half.
|
||||||
|
|
||||||
current = Split(current, address);
|
region = Split(region, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.EndAddress > address + size)
|
if (region.EndAddress > address + size)
|
||||||
{
|
{
|
||||||
// Split the region around our end address and take the low half.
|
// Split the region around our end address and take the low half.
|
||||||
|
|
||||||
Split(current, address + size);
|
Split(region, address + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
list.Add(current);
|
list.Add(region);
|
||||||
lastAddress = current.EndAddress;
|
lastAddress = region.EndAddress;
|
||||||
current = current.Next;
|
current = current.Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +374,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
private T Split(T region, ulong splitAddress)
|
private T Split(T region, ulong splitAddress)
|
||||||
{
|
{
|
||||||
T newRegion = (T)region.Split(splitAddress);
|
T newRegion = (T)region.Split(splitAddress);
|
||||||
|
Update(region);
|
||||||
Add(newRegion);
|
Add(newRegion);
|
||||||
return newRegion;
|
return newRegion;
|
||||||
}
|
}
|
||||||
@@ -320,11 +386,16 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>The leftmost overlapping item, or null if none is found</returns>
|
/// <returns>The leftmost overlapping item, or null if none is found</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override T FindOverlap(ulong address, ulong size)
|
public override RangeItem<T> FindOverlap(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
int index = BinarySearchLeftEdge(address, address + size);
|
int index = BinarySearchLeftEdge(address, address + size);
|
||||||
|
|
||||||
return index < 0 ? null : Items[index];
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Items[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -334,11 +405,16 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>The overlapping item, or null if none is found</returns>
|
/// <returns>The overlapping item, or null if none is found</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override T FindOverlapFast(ulong address, ulong size)
|
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
int index = BinarySearch(address, address + size);
|
int index = BinarySearch(address, address + size);
|
||||||
|
|
||||||
return index < 0 ? null : Items[index];
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Items[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -348,18 +424,23 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>The first and last overlapping items, or null if none are found</returns>
|
/// <returns>The first and last overlapping items, or null if none are found</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public (T, T) FindOverlapsAsNodes(ulong address, ulong size)
|
public (RangeItem<T>, RangeItem<T>) FindOverlapsAsNodes(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
return index < 0 ? (null, null) : (Items[index], Items[endIndex - 1]);
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Items[index], Items[endIndex - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[] FindOverlapsAsArray(ulong address, ulong size, out int length)
|
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size, out int length)
|
||||||
{
|
{
|
||||||
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
T[] result;
|
RangeItem<T>[] result;
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
@@ -368,20 +449,29 @@ namespace Ryujinx.Memory.Range
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = ArrayPool<T>.Shared.Rent(endIndex - index);
|
result = ArrayPool<RangeItem<T>>.Shared.Rent(endIndex - index);
|
||||||
length = endIndex - index;
|
length = endIndex - index;
|
||||||
|
|
||||||
Items.AsSpan(index, endIndex - index).CopyTo(result);
|
Array.Copy(Items, index, result, 0, endIndex - index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<T> FindOverlapsAsSpan(ulong address, ulong size)
|
public Span<RangeItem<T>> FindOverlapsAsSpan(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
ReadOnlySpan<T> result = index < 0 ? [] : Items.AsSpan(index, endIndex - index);
|
Span<RangeItem<T>> result;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = Items.AsSpan().Slice(index, endIndex - index);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -390,7 +480,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < Count; i++)
|
for (int i = 0; i < Count; i++)
|
||||||
{
|
{
|
||||||
yield return Items[i];
|
yield return Items[i].Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// startIndex is inclusive.
|
/// startIndex is inclusive.
|
||||||
/// endIndex is exclusive.
|
/// endIndex is exclusive.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public readonly struct OverlapResult<T> where T : class, IRangeListRange<T>
|
public readonly struct OverlapResult<T> where T : IRange
|
||||||
{
|
{
|
||||||
public readonly int StartIndex = -1;
|
public readonly int StartIndex = -1;
|
||||||
public readonly int EndIndex = -1;
|
public readonly int EndIndex = -1;
|
||||||
public readonly T QuickResult;
|
public readonly RangeItem<T> QuickResult;
|
||||||
public int Count => EndIndex - StartIndex;
|
public int Count => EndIndex - StartIndex;
|
||||||
|
|
||||||
public OverlapResult(int startIndex, int endIndex, T quickResult = null)
|
public OverlapResult(int startIndex, int endIndex, RangeItem<T> quickResult = null)
|
||||||
{
|
{
|
||||||
this.StartIndex = startIndex;
|
this.StartIndex = startIndex;
|
||||||
this.EndIndex = endIndex;
|
this.EndIndex = endIndex;
|
||||||
@@ -33,7 +33,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// Sorted list of ranges that supports binary search.
|
/// Sorted list of ranges that supports binary search.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the range.</typeparam>
|
/// <typeparam name="T">Type of the range.</typeparam>
|
||||||
public class RangeList<T> : RangeListBase<T> where T : class, IRangeListRange<T>
|
public class RangeList<T> : RangeListBase<T> where T : IRange
|
||||||
{
|
{
|
||||||
public readonly ReaderWriterLockSlim Lock = new();
|
public readonly ReaderWriterLockSlim Lock = new();
|
||||||
|
|
||||||
@@ -61,6 +61,104 @@ namespace Ryujinx.Memory.Range
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Insert(index, new RangeItem<T>(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an item's end address on the list. Address must be the same.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to be updated</param>
|
||||||
|
/// <returns>True if the item was located and updated, false otherwise</returns>
|
||||||
|
protected override bool Update(T item)
|
||||||
|
{
|
||||||
|
int index = BinarySearch(item.Address);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
while (index < Count)
|
||||||
|
{
|
||||||
|
if (Items[index].Value.Equals(item))
|
||||||
|
{
|
||||||
|
RangeItem<T> rangeItem = new(item) { Previous = Items[index].Previous, Next = Items[index].Next };
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Items[index - 1].Next = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < Count - 1)
|
||||||
|
{
|
||||||
|
Items[index + 1].Previous = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Items[index].Address > item.Address)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an item's end address on the list. Address must be the same.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The RangeItem to be updated</param>
|
||||||
|
/// <returns>True if the item was located and updated, false otherwise</returns>
|
||||||
|
protected override bool Update(RangeItem<T> item)
|
||||||
|
{
|
||||||
|
int index = BinarySearch(item.Address);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
while (index < Count)
|
||||||
|
{
|
||||||
|
if (Items[index].Equals(item))
|
||||||
|
{
|
||||||
|
RangeItem<T> rangeItem = new(item.Value) { Previous = item.Previous, Next = item.Next };
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Items[index - 1].Next = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < Count - 1)
|
||||||
|
{
|
||||||
|
Items[index + 1].Previous = rangeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Items[index].Address > item.Address)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void Insert(int index, RangeItem<T> item)
|
||||||
|
{
|
||||||
|
Debug.Assert(item.Address != item.EndAddress);
|
||||||
|
|
||||||
|
Debug.Assert(item.Address % 32 == 0);
|
||||||
|
|
||||||
if (Count + 1 > Items.Length)
|
if (Count + 1 > Items.Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref Items, Items.Length + BackingGrowthSize);
|
Array.Resize(ref Items, Items.Length + BackingGrowthSize);
|
||||||
@@ -122,7 +220,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="startItem">The first item in the range of items to be removed</param>
|
/// <param name="startItem">The first item in the range of items to be removed</param>
|
||||||
/// <param name="endItem">The last item in the range of items to be removed</param>
|
/// <param name="endItem">The last item in the range of items to be removed</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override void RemoveRange(T startItem, T endItem)
|
public override void RemoveRange(RangeItem<T> startItem, RangeItem<T> endItem)
|
||||||
{
|
{
|
||||||
if (startItem is null)
|
if (startItem is null)
|
||||||
{
|
{
|
||||||
@@ -131,29 +229,30 @@ namespace Ryujinx.Memory.Range
|
|||||||
|
|
||||||
if (startItem == endItem)
|
if (startItem == endItem)
|
||||||
{
|
{
|
||||||
Remove(startItem);
|
Remove(startItem.Value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(int index, int endIndex) = BinarySearchEdges(startItem.Address, endItem.EndAddress);
|
int startIndex = BinarySearch(startItem.Address);
|
||||||
|
int endIndex = BinarySearch(endItem.Address);
|
||||||
|
|
||||||
if (endIndex < Count)
|
if (endIndex < Count - 1)
|
||||||
{
|
{
|
||||||
Items[endIndex].Previous = index > 0 ? Items[index - 1] : null;
|
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > 0)
|
if (startIndex > 0)
|
||||||
{
|
{
|
||||||
Items[index - 1].Next = endIndex < Count ? Items[endIndex] : null;
|
Items[startIndex - 1].Next = endIndex < Count - 1 ? Items[endIndex + 1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (endIndex < Count)
|
if (endIndex < Count - 1)
|
||||||
{
|
{
|
||||||
Array.Copy(Items, endIndex, Items, index, Count - endIndex);
|
Array.Copy(Items, endIndex + 1, Items, startIndex, Count - endIndex - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Count -= endIndex - index;
|
Count -= endIndex - startIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -169,7 +268,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
while (index < Count)
|
while (index < Count)
|
||||||
{
|
{
|
||||||
if (Items[index] == item)
|
if (Items[index].Value.Equals(item))
|
||||||
{
|
{
|
||||||
RemoveAt(index);
|
RemoveAt(index);
|
||||||
|
|
||||||
@@ -199,7 +298,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>The overlapping item, or the default value for the type if none found</returns>
|
/// <returns>The overlapping item, or the default value for the type if none found</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override T FindOverlap(ulong address, ulong size)
|
public override RangeItem<T> FindOverlap(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
int index = BinarySearchLeftEdge(address, address + size);
|
int index = BinarySearchLeftEdge(address, address + size);
|
||||||
|
|
||||||
@@ -222,7 +321,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>The overlapping item, or the default value for the type if none found</returns>
|
/// <returns>The overlapping item, or the default value for the type if none found</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override T FindOverlapFast(ulong address, ulong size)
|
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
int index = BinarySearch(address, address + size);
|
int index = BinarySearch(address, address + size);
|
||||||
|
|
||||||
@@ -241,7 +340,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
|
/// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
|
||||||
/// <returns>Range information of overlapping items found</returns>
|
/// <returns>Range information of overlapping items found</returns>
|
||||||
private OverlapResult<T> FindOverlaps(ulong address, ulong size, ref T[] output)
|
private OverlapResult<T> FindOverlaps(ulong address, ulong size, ref RangeItem<T>[] output)
|
||||||
{
|
{
|
||||||
int outputCount = 0;
|
int outputCount = 0;
|
||||||
|
|
||||||
@@ -254,7 +353,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
|
|
||||||
for (int i = startIndex; i < Count; i++)
|
for (int i = startIndex; i < Count; i++)
|
||||||
{
|
{
|
||||||
T item = Items[i];
|
ref RangeItem<T> item = ref Items[i];
|
||||||
|
|
||||||
if (item.Address >= endAddress)
|
if (item.Address >= endAddress)
|
||||||
{
|
{
|
||||||
@@ -299,7 +398,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < Count; i++)
|
for (int i = 0; i < Count; i++)
|
||||||
{
|
{
|
||||||
yield return Items[i];
|
yield return Items[i].Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user