Compare commits

..

No commits in common. "master" and "Canary-1.3.228" have entirely different histories.

54 changed files with 1028 additions and 1026 deletions

View File

@ -1,4 +1,4 @@
name: Canary CI
name: Canary release job
on:
workflow_dispatch:
@ -19,6 +19,7 @@ concurrency: release
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.3"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
RELEASE: 1
@ -29,8 +30,8 @@ jobs:
strategy:
matrix:
platform:
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
- { 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:
@ -43,25 +44,11 @@ jobs:
- name: Overwrite csc problem matcher
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
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 "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
@ -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
- name: Packing Windows builds
if: contains(matrix.platform.name, 'win')
if: matrix.platform.os == 'windows-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -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
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: contains(matrix.platform.name, 'linux')
if: matrix.platform.os == 'ubuntu-latest'
run: |
pushd publish
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
popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -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
- name: Build AppImage (Linux)
if: contains(matrix.platform.name, 'linux')
if: matrix.platform.os == 'ubuntu-latest'
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
@ -139,8 +139,8 @@ jobs:
pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -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
macos_release:
@ -159,10 +159,10 @@ jobs:
chmod +x llvm.sh
sudo ./llvm.sh 17
- name: Install gli
- name: Install GitLabCli
run: |
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
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
@ -183,10 +183,9 @@ jobs:
- 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 "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: Configure for release
run: |
@ -201,7 +200,7 @@ jobs:
- name: Publish macOS Ryujinx
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
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -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:
name: Create GitLab Release
@ -211,41 +210,37 @@ jobs:
- release
steps:
- 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: |
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
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
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
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
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
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
run: |
gli refresh-version-cache -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Canary
- name: Advance to the next version
run: |
gli increment-version -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 }}'

224
.github/workflows/debug_release.yml vendored Normal file
View 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"

View File

@ -1,18 +1,15 @@
name: Stable CI
name: Release job
on:
workflow_dispatch:
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
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
@ -23,8 +20,8 @@ jobs:
strategy:
matrix:
platform:
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
- { 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:
@ -36,30 +33,12 @@ jobs:
- name: Overwrite csc problem matcher
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
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 "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
@ -79,34 +58,47 @@ 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
- name: Packing Windows builds
if: contains(matrix.platform.name, 'win')
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
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -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
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: contains(matrix.platform.name, 'linux')
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 upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -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
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build AppImage (Linux)
if: contains(matrix.platform.name, 'linux')
if: matrix.platform.os == 'ubuntu-latest'
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
@ -139,7 +131,7 @@ jobs:
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -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
macos_release:
@ -158,10 +150,10 @@ jobs:
chmod +x llvm.sh
sudo ./llvm.sh 17
- name: Install gli
- name: Install GitLabCli
run: |
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
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
@ -182,14 +174,9 @@ jobs:
- 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 "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: Configure for release
run: |
@ -202,8 +189,7 @@ jobs:
- 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 upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -p publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
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
@ -214,45 +200,32 @@ jobs:
steps:
- 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: |
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
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
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
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
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
run: |
gli refresh-version-cache -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Stable
- 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
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=stable' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'

1
.gitignore vendored
View File

@ -100,7 +100,6 @@ DocProject/Help/html
# Click-Once directory
publish/
RyubingMaintainerTools/
# Publish Web Output
*.Publish.xml

View File

@ -19,11 +19,6 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
/// </summary>
public Array6<byte> Output;
/// <summary>
/// Reserved/unused.
/// </summary>
private readonly uint _padding;
/// <summary>
/// Biquad filter numerator (b0, b1, b2).

View File

@ -7,9 +7,6 @@ namespace Ryujinx.Common.Memory
{
private static readonly RecyclableMemoryStreamManager _shared = new();
private static readonly ObjectPool<RecyclableMemoryStream> _streamPool =
new(() => new RecyclableMemoryStream(_shared, Guid.NewGuid(), null, 0));
/// <summary>
/// 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
@ -22,12 +19,7 @@ namespace Ryujinx.Common.Memory
/// </summary>
/// <returns>A <c>RecyclableMemoryStream</c></returns>
public static RecyclableMemoryStream GetStream()
{
RecyclableMemoryStream stream = _streamPool.Allocate();
stream.SetLength(0);
return stream;
}
=> new(_shared);
/// <summary>
/// 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;
try
{
stream = _streamPool.Allocate();
stream.SetLength(0);
stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
stream.Write(buffer);
stream.Position = 0;
return stream;
@ -92,8 +83,7 @@ namespace Ryujinx.Common.Memory
RecyclableMemoryStream stream = null;
try
{
stream = _streamPool.Allocate();
stream.SetLength(0);
stream = new RecyclableMemoryStream(_shared, id, tag, count);
stream.Write(buffer, offset, count);
stream.Position = 0;
return stream;
@ -104,11 +94,6 @@ namespace Ryujinx.Common.Memory
throw;
}
}
public static void ReleaseStream(RecyclableMemoryStream stream)
{
_streamPool.Release(stream);
}
}
}
}

View File

@ -20,21 +20,5 @@ namespace Ryujinx.Common.Utilities
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);
}
}
}
}

View File

@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.GAL
void SetRasterizerDiscard(bool discard);
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil);
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
void SetScissors(ReadOnlySpan<Rectangle<int>> regions);

View File

@ -1,6 +1,5 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using System;
using System.Buffers;
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 readonly CommandType CommandType => CommandType.SetRenderTargets;
private int _colorsCount;
private TableRef<ITexture[]> _colors;
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;
_depthStencil = depthStencil;
}
@ -23,15 +20,16 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
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);
}
}

View File

@ -267,12 +267,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_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);
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();
}

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
/// </summary>
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 QueueCount = 10000;

View File

@ -404,12 +404,9 @@ namespace Ryujinx.Graphics.Gpu
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))
{
SyncActions.RemoveAt(i--);
}
action.SyncPreAction(syncPoint);
}
foreach (ISyncActionHandler action in SyncpointActions)

View File

@ -411,7 +411,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// flushes often enough, which is determined by the flush balance.
/// </summary>
/// <inheritdoc/>
public bool SyncPreAction(bool syncpoint)
public void SyncPreAction(bool syncpoint)
{
if (syncpoint || NextSyncCopies())
{
@ -421,8 +421,6 @@ namespace Ryujinx.Graphics.Gpu.Image
_registeredBufferSync = _modifiedSync;
}
}
return true;
}
/// <summary>

View File

@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
/// </summary>
class Buffer : INonOverlappingRange<Buffer>, ISyncActionHandler, IDisposable
class Buffer : INonOverlappingRange, ISyncActionHandler, IDisposable
{
private const ulong GranularBufferThreshold = 4096;
@ -41,9 +41,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// End address of the buffer in guest memory.
/// </summary>
public ulong EndAddress => Address + Size;
public Buffer Next { get; set; }
public Buffer Previous { get; set; }
/// <summary>
/// Increments when the buffer is (partially) unmapped or disposed.
@ -90,7 +87,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly bool _useGranular;
private bool _syncActionRegistered;
private bool _bufferInherited;
private int _referenceCount = 1;
@ -117,7 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong size,
BufferStage stage,
bool sparseCompatible,
Buffer[] baseBuffers)
RangeItem<Buffer>[] baseBuffers)
{
_context = context;
_physicalMemory = physicalMemory;
@ -138,15 +134,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (baseBuffers.Length != 0)
{
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
{
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.
/// </summary>
/// <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>
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();
}
@ -393,16 +389,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// This will copy any buffer ranges designated for pre-flushing.
/// </summary>
/// <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)
{
return false;
return;
}
if (BackingState.ShouldChangeBacking())
@ -419,8 +410,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, _syncPreRangeAction);
}
}
return false;
}
void SyncPreRangeAction(ulong address, ulong size)
@ -437,13 +426,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_syncActionRegistered = false;
if (_bufferInherited)
{
return true;
}
if (_useGranular)
{
_modifiedRanges?.GetRanges(Address, Size, _syncRangeAction);
}
else
@ -467,8 +453,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="from">The buffer to inherit from</param>
public void InheritModifiedRanges(Buffer from)
{
from._bufferInherited = true;
if (from._modifiedRanges is { HasRanges: true })
{
if (from._syncActionRegistered && !_syncActionRegistered)

View File

@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="parent">Parent buffer</param>
/// <param name="stage">Initial buffer stage</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;
_systemMemoryType = context.Capabilities.MemoryType;
@ -102,9 +102,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (baseBuffers.Length != 0)
{
foreach (Buffer item in baseBuffers)
foreach (RangeItem<Buffer> item in baseBuffers)
{
CombineState(item.BackingState);
CombineState(item.Value.BackingState);
}
}
}

View File

@ -79,13 +79,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int index = 0; index < range.Count; 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++)
{
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 alignedSize = alignedEndAddress - alignedAddress;
Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize);
Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize).Value;
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
@ -392,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
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);
physicalBuffers.Add(buffer);
@ -484,7 +487,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param>
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)
{
@ -515,7 +521,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
// 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.
ulong existingSize = overlaps[0].Size;
ulong existingSize = overlaps[0].Value.Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize);
@ -529,22 +535,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
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.Lock.ExitWriteLock();
ulong newSize = endAddress - address;
_buffers.Add(CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray));
newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray);
}
else
{
_buffers.Lock.ExitWriteLock();
}
}
else
{
_buffers.Lock.ExitWriteLock();
// 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)
{
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)
{
@ -573,7 +598,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (overlaps[0].Address > address ||
overlaps[0].EndAddress < endAddress ||
(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.
// However, after the range is aligned, it is possible that it
@ -597,18 +622,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong newSize = endAddress - address;
Buffer[] overlapsArray = overlaps.ToArray();
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
_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
{
_buffers.Lock.ExitWriteLock();
// 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();
}
}
/// <summary>
@ -621,13 +663,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <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="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);
for (int index = 0; index < overlaps.Length; index++)
{
Buffer buffer = overlaps[index];
Buffer buffer = overlaps[index].Value;
int dstOffset = (int)(buffer.Address - newBuffer.Address);
@ -855,7 +897,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
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);
@ -903,7 +945,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (size != 0)
{
buffer = _buffers.FindOverlap(address, size);
buffer = _buffers.FindOverlap(address, size).Value;
buffer.CopyFromDependantVirtualBuffers();
buffer.SynchronizeMemory(address, size);
@ -915,7 +957,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
buffer = _buffers.FindOverlapFast(address, 1);
buffer = _buffers.FindOverlapFast(address, 1).Value;
}
return buffer;
@ -953,7 +995,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (size != 0)
{
Buffer buffer = _buffers.FindOverlap(address, size);
Buffer buffer = _buffers.FindOverlap(address, size).Value;
if (copyBackVirtual)
{

View File

@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// A range within a buffer that has been modified by the GPU.
/// </summary>
class BufferModifiedRange : INonOverlappingRange<BufferModifiedRange>
class BufferModifiedRange : INonOverlappingRange
{
/// <summary>
/// Start address of the range in guest memory.
@ -24,9 +24,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// End address of the range in guest memory.
/// </summary>
public ulong EndAddress => Address + Size;
public BufferModifiedRange Next { get; set; }
public BufferModifiedRange Previous { get; set; }
/// <summary>
/// The GPU sync number at the time of the last modification.
@ -57,14 +54,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Checks if a given range overlaps with the modified range.
/// </summary>
/// <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>
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();
}
@ -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.
Lock.EnterReadLock();
ReadOnlySpan<BufferModifiedRange> overlaps = FindOverlapsAsSpan(address, size);
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
for (int i = 0; i < overlaps.Length; i++)
{
BufferModifiedRange overlap = overlaps[i];
BufferModifiedRange overlap = overlaps[i].Value;
if (overlap.Address > address)
{
@ -160,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong syncNumber = _context.SyncNumber;
// We may overlap with some existing modified regions. They must be cut into by the new entry.
Lock.EnterWriteLock();
(BufferModifiedRange first, BufferModifiedRange last) = FindOverlapsAsNodes(address, size);
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null)
{
@ -173,39 +170,34 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (first.Address == address && first.EndAddress == endAddress)
{
first.SyncNumber = syncNumber;
first.Parent = this;
first.Value.SyncNumber = syncNumber;
first.Value.Parent = this;
Lock.ExitWriteLock();
return;
}
if (first.Address < address)
{
first.Value.Size = address - first.Address;
Update(first);
if (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
{
if (first.EndAddress > endAddress)
{
first.Size = first.EndAddress - endAddress;
first.Address = endAddress;
first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress;
Update(first);
}
else
{
first.Address = address;
first.Size = size;
first.SyncNumber = syncNumber;
first.Parent = this;
Lock.ExitWriteLock();
return;
Remove(first.Value);
}
}
@ -215,39 +207,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
return;
}
BufferModifiedRange buffPre = null;
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address)
{
first.Size = address - first.Address;
first = first.Next;
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
first.Value.SyncNumber, first.Value.Parent);
extendsPre = true;
}
if (last.EndAddress > endAddress)
{
last.Size = last.EndAddress - endAddress;
last.Address = endAddress;
last = last.Previous;
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
last.Value.SyncNumber, last.Value.Parent);
extendsPost = true;
}
if (first.Address < last.Address)
RemoveRange(first, last);
if (extendsPre)
{
RemoveRange(first.Next, last);
first.Address = address;
first.Size = size;
first.SyncNumber = syncNumber;
first.Parent = this;
Add(buffPre);
}
else if (first.Address == last.Address)
if (extendsPost)
{
first.Address = address;
first.Size = size;
first.SyncNumber = syncNumber;
first.Parent = this;
Add(buffPost);
}
else
{
Add(new BufferModifiedRange(address, size, syncNumber, this));
}
Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock();
}
@ -261,11 +252,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
{
Lock.EnterReadLock();
ReadOnlySpan<BufferModifiedRange> overlaps = FindOverlapsAsSpan(address, size);
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
for (int i = 0; i < overlaps.Length; i++)
{
BufferModifiedRange overlap = overlaps[i];
BufferModifiedRange overlap = overlaps[i].Value;
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.
Lock.EnterReadLock();
BufferModifiedRange[] overlaps = FindOverlapsAsArray(address, size, out int length);
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int length);
Lock.ExitReadLock();
if (length != 0)
{
for (int i = 0; i < length; i++)
{
BufferModifiedRange overlap = overlaps[i];
BufferModifiedRange overlap = overlaps[i].Value;
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)
{
Lock.EnterReadLock();
BufferModifiedRange first = FindOverlapFast(address, size);
RangeItem<BufferModifiedRange> first = FindOverlapFast(address, size);
bool result = first is not null;
Lock.ExitReadLock();
return result;
@ -345,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="address">The start address of the flush range</param>
/// <param name="endAddress">The end address of the flush range</param>
private void RemoveRangesAndFlush(
BufferModifiedRange[] overlaps,
RangeItem<BufferModifiedRange>[] overlaps,
int rangeCount,
long highestDiff,
ulong currentSync,
@ -358,7 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++)
{
BufferModifiedRange overlap = overlaps[i];
BufferModifiedRange overlap = overlaps[i].Value;
long diff = (long)(overlap.SyncNumber - currentSync);
@ -367,14 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong clampAddress = Math.Max(address, overlap.Address);
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
if (i == 0 || i == rangeCount - 1)
{
ClearPart(overlap, clampAddress, clampEnd);
}
else
{
Remove(overlap);
}
ClearPart(overlap, clampAddress, clampEnd);
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction);
}
@ -414,7 +398,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
Lock.EnterWriteLock();
// 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)
{
@ -430,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++)
{
BufferModifiedRange overlap = overlaps![i];
BufferModifiedRange overlap = overlaps![i].Value;
long diff = (long)(overlap.SyncNumber - currentSync);
@ -452,7 +436,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
ArrayPool<BufferModifiedRange>.Shared.Return(overlaps!);
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps!);
Lock.ExitWriteLock();
}
@ -468,9 +452,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction)
{
ranges.Lock.EnterReadLock();
int rangesCount = ranges.Count;
BufferModifiedRange[] inheritRanges = ArrayPool<BufferModifiedRange>.Shared.Rent(ranges.Count);
ranges.Items.AsSpan(0, ranges.Count).CopyTo(inheritRanges);
BufferModifiedRange[] inheritRanges = ranges.ToArray();
ranges.Lock.ExitReadLock();
// Copy over the migration from the previous range list
@ -496,26 +478,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
ranges._migrationTarget = this;
Lock.EnterWriteLock();
for (int i = 0; i < rangesCount; i++)
foreach (BufferModifiedRange range in inheritRanges)
{
BufferModifiedRange range = inheritRanges[i];
Add(range);
}
Lock.ExitWriteLock();
ulong currentSync = _context.SyncNumber;
for (int i = 0; i < rangesCount; i++)
foreach (BufferModifiedRange range in inheritRanges)
{
BufferModifiedRange range = inheritRanges[i];
if (range.SyncNumber != currentSync)
{
registerRangeAction(range.Address, range.Size);
}
}
ArrayPool<BufferModifiedRange>.Shared.Return(inheritRanges);
}
/// <summary>
@ -556,25 +534,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
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 (overlap.Address < address)
{
if (overlap.EndAddress > endAddress)
{
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
}
overlap.Size = address - overlap.Address;
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent));
}
else if (overlap.EndAddress > endAddress)
if (overlap.EndAddress > endAddress)
{
overlap.Size = overlap.EndAddress - endAddress;
overlap.Address = endAddress;
}
else
{
Remove(overlap);
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
}
}
@ -587,7 +558,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
ulong endAddress = address + size;
Lock.EnterWriteLock();
(BufferModifiedRange first, BufferModifiedRange last) = FindOverlapsAsNodes(address, size);
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null)
{
@ -599,24 +570,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (first.Address < address)
{
first.Size = address - first.Address;
first.Value.Size = address - first.Address;
Update(first);
if (first.EndAddress > endAddress)
{
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.SyncNumber, first.Parent));
first.Value.SyncNumber, first.Value.Parent));
}
}
else
{
if (first.EndAddress > endAddress)
{
first.Size = first.EndAddress - endAddress;
first.Address = endAddress;
first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress;
Update(first);
}
else
{
Remove(first);
Remove(first.Value);
}
}
@ -632,14 +605,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (first.Address < address)
{
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
first.SyncNumber, first.Parent);
first.Value.SyncNumber, first.Value.Parent);
extendsPre = true;
}
if (last.EndAddress > endAddress)
{
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
last.SyncNumber, last.Parent);
last.Value.SyncNumber, last.Value.Parent);
extendsPost = true;
}

View File

@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Represents a GPU virtual memory range.
/// </summary>
private class VirtualRange : INonOverlappingRange<VirtualRange>
private class VirtualRange : INonOverlappingRange
{
/// <summary>
/// GPU virtual address where the range starts.
@ -32,9 +32,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public ulong EndAddress => Address + Size;
public VirtualRange Next { get; set; }
public VirtualRange Previous { get; set; }
/// <summary>
/// Physical regions where the GPU virtual region is mapped.
/// </summary>
@ -57,14 +54,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Checks if a given range overlaps with the buffer.
/// </summary>
/// <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>
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();
}
@ -125,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong originalVa = gpuVa;
_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)
{
@ -150,8 +147,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
found = first.Range.Count == 1 || IsSparseAligned(first.Range);
range = first.Range.Slice(gpuVa - first.Address, size);
found = first.Value.Range.Count == 1 || IsSparseAligned(first.Value.Range);
range = first.Value.Range.Slice(gpuVa - first.Address, size);
}
}
else

View File

@ -17,6 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
/// Action to be performed immediately before sync is created.
/// </summary>
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
bool SyncPreAction(bool syncpoint) { return true; }
void SyncPreAction(bool syncpoint) { }
}
}

View File

@ -1166,7 +1166,7 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil)
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{
EnsureFramebuffer();

View File

@ -71,31 +71,17 @@ namespace Ryujinx.Graphics.Vulkan
HasDepthStencil = isDepthStencil;
}
public FramebufferParams(Device device, ReadOnlySpan<ITexture> colors, ITexture depthStencil)
public FramebufferParams(Device device, ITexture[] colors, ITexture depthStencil)
{
_device = device;
int colorsCount = 0;
_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 colorsCount = colors.Count(IsValidTextureView);
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
_attachments = new Auto<DisposableImageView>[count];
_colors = new TextureView[colorsCount];
_colorsCanonical = colors.Select(color => color is TextureView view && view.Valid ? view : null).ToArray();
AttachmentSamples = new uint[count];
AttachmentFormats = new VkFormat[count];
@ -179,17 +165,9 @@ namespace Ryujinx.Graphics.Vulkan
_totalCount = colors.Length;
}
public FramebufferParams Update(ReadOnlySpan<ITexture> colors, ITexture depthStencil)
public FramebufferParams Update(ITexture[] colors, ITexture depthStencil)
{
int colorsCount = 0;
foreach (ITexture color in colors)
{
if (IsValidTextureView(color))
{
colorsCount++;
}
}
int colorsCount = colors.Count(IsValidTextureView);
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);

View File

@ -1,7 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Silk.NET.Vulkan;
using System;
using System.Buffers;
namespace Ryujinx.Graphics.Vulkan
{
@ -10,8 +10,6 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary>
class MultiFenceHolder
{
public static readonly ObjectPool<FenceHolder[]> FencePool = new(() => new FenceHolder[CommandBufferPool.MaxCommandBuffers]);
private const int BufferUsageTrackingGranularity = 4096;
public FenceHolder[] Fences { get; }
@ -22,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary>
public MultiFenceHolder()
{
Fences = FencePool.Allocate();
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
}
/// <summary>
@ -31,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <param name="size">Size of the buffer</param>
public MultiFenceHolder(int size)
{
Fences = FencePool.Allocate();
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
}

View File

@ -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);
CreateRenderPass();
@ -1043,7 +1043,7 @@ namespace Ryujinx.Graphics.Vulkan
SignalAttachmentChange();
}
public void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil)
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{
_framebufferUsingColorWriteMask = false;
SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
@ -1389,7 +1389,7 @@ namespace Ryujinx.Graphics.Vulkan
_currentPipelineHandle = 0;
}
private void CreateFramebuffer(Span<ITexture> colors, ITexture depthStencil, bool filterWriteMasked)
private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{
if (filterWriteMasked)
{
@ -1399,7 +1399,7 @@ namespace Ryujinx.Graphics.Vulkan
// Just try to remove duplicate attachments.
// Save a copy of the array to rebind when mask changes.
void MaskOut(ReadOnlySpan<ITexture> colors)
void MaskOut()
{
if (!_framebufferUsingColorWriteMask)
{
@ -1436,12 +1436,12 @@ namespace Ryujinx.Graphics.Vulkan
if (vkBlend.ColorWriteMask == 0)
{
colors[i] = null;
MaskOut(colors);
MaskOut();
}
else if (vkBlend2.ColorWriteMask == 0)
{
colors[j] = null;
MaskOut(colors);
MaskOut();
}
}
}

View File

@ -1,6 +1,6 @@
using Ryujinx.Common.Logging;
using Silk.NET.Vulkan;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -193,8 +193,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_firstHandle = first.ID + 1;
_handles.RemoveAt(0);
Array.Clear(first.Waitable.Fences);
MultiFenceHolder.FencePool.Release(first.Waitable.Fences);
ArrayPool<FenceHolder>.Shared.Return(first.Waitable.Fences);
first.Waitable = null;
}
}

View File

@ -1,91 +1,43 @@
using Gommon;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Memory;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Ryujinx.HLE.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()
{
_rcmdDelegates.Add(new RcmdEntry(
["help"],
(dbgr, _) => _rcmdDelegates
.Where(entry => entry.HelpLines.Length > 0)
.SelectMany(entry => entry.HelpLines)
.JoinToString('\n') + '\n',
Array.Empty<string>()));
_rcmdDelegates.Add(new RcmdEntry(["get info"], (dbgr, _) => dbgr.GetProcessInfo(), ["get info"]));
_rcmdDelegates.Add(new RcmdEntry(["backtrace", "bt"], (dbgr, _) => dbgr.GetStackTrace(), ["backtrace", "bt"]));
_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}"]));
_rcmdDelegates.Add(["help"],
_ => _rcmdDelegates.Keys
.Where(x => !x[0].Equals("help"))
.Select(x => x.JoinToString('\n'))
.JoinToString('\n') + '\n'
);
_rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo());
_rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace());
_rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters());
_rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump());
}
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;
string trimmedCommand = originalCommand.Trim();
Func<Debugger, string> searchResult = _ => $"Unknown command: {command}\n";
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))
{
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);
}
searchResult = dlg;
break;
}
}
return $"Unknown command: {originalCommand}\n";
return searchResult;
}
public string GetStackTrace()
@ -134,181 +86,5 @@ namespace Ryujinx.HLE.Debugger
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}";
}
}
}

View File

@ -404,8 +404,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
string command = Helpers.FromHex(hexCommand);
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
string response = Debugger.CallRcmdDelegate(Debugger, command);
Processor.ReplyHex(response);
Func<Debugger, string> rcmd = Debugger.FindRcmdDelegate(command);
Processor.ReplyHex(rcmd(Debugger));
}
catch (Exception e)
{

View File

@ -2,7 +2,6 @@ using Microsoft.IO;
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@ -38,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Ipc
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr)
{
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data);
using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data);
BinaryReader reader = new(ms);
@ -124,8 +123,6 @@ namespace Ryujinx.HLE.HOS.Ipc
}
ObjectIds = [];
MemoryStreamManager.Shared.ReleaseStream(ms);
}
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)

View File

@ -20,15 +20,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
_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)
{
return Add(_sendBufferDescriptors, src, dst, size, state);

View File

@ -1,4 +1,3 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
@ -33,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize);
KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize);
KernelContext.CriticalSection.Enter();
@ -56,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
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();

View File

@ -10,8 +10,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KServerSession : KSynchronizationObject
{
public readonly ObjectPool<KSessionRequest> RequestPool = new(() => new KSessionRequest());
private static readonly MemoryState[] _ipcMemoryStates =
[
MemoryState.IpcBuffer3,
@ -276,8 +274,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KernelContext.CriticalSection.Leave();
WakeClientThread(request, clientResult);
RequestPool.Release(request);
}
if (clientHeader.ReceiveListType < 2 &&
@ -631,8 +627,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
CloseAllHandles(clientMsg, serverHeader, clientProcess);
FinishRequest(request, clientResult);
RequestPool.Release(request);
}
if (clientHeader.ReceiveListType < 2 &&
@ -871,8 +865,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
// Unmap buffers from server.
FinishRequest(request, clientResult);
RequestPool.Release(request);
return serverResult;
}
@ -1106,8 +1098,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
{
FinishRequest(request, KernelResult.PortRemoteClosed);
RequestPool.Release(request);
}
}
@ -1127,8 +1117,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
}
RequestPool.Release(request);
}
WakeServerThreads(KernelResult.PortRemoteClosed);

View File

@ -5,18 +5,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
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 KWritableEvent AsyncEvent { get; private set; }
public KWritableEvent AsyncEvent { get; }
public ulong CustomCmdBuffAddr { get; private set; }
public ulong CustomCmdBuffSize { get; private set; }
public ulong CustomCmdBuffAddr { get; }
public ulong CustomCmdBuffSize { get; }
public KSessionRequest Set(
public KSessionRequest(
KThread clientThread,
ulong customCmdBuffAddr,
ulong customCmdBuffSize,
@ -27,9 +27,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
CustomCmdBuffSize = customCmdBuffSize;
AsyncEvent = asyncEvent;
BufferDescriptorTable = BufferDescriptorTable?.Clear() ?? new KBufferDescriptorTable();
return this;
BufferDescriptorTable = new KBufferDescriptorTable();
}
}
}

View File

@ -1,8 +1,10 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Horizon.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
@ -10,12 +12,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KAddressArbiter
{
private const int HasListenersMask = 0x40000000;
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
private readonly KernelContext _context;
private readonly Dictionary<ulong, List<KThread>> _condVarThreads;
private readonly Dictionary<ulong, List<KThread>> _arbiterThreads;
private readonly ByDynamicPriority _byDynamicPriority;
private readonly List<KThread> _condVarThreads;
private readonly List<KThread> _arbiterThreads;
public KAddressArbiter(KernelContext context)
{
@ -23,7 +25,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_condVarThreads = [];
_arbiterThreads = [];
_byDynamicPriority = new ByDynamicPriority();
}
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
@ -139,23 +140,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexAddress = mutexAddress;
currentThread.ThreadHandleForUserMutex = threadHandle;
currentThread.CondVarAddress = condVarAddress;
if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads))
{
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]);
}
_condVarThreads.Add(currentThread);
if (timeout != 0)
{
@ -178,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexOwner?.RemoveMutexWaiter(currentThread);
_condVarThreads[condVarAddress].Remove(currentThread);
_condVarThreads.Remove(currentThread);
_context.CriticalSection.Leave();
@ -213,14 +200,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
int validThreads = 0;
_condVarThreads.TryGetValue(address, out List<KThread> threads);
if (threads is not null && threads.Count > 0)
static bool SignalProcessWideKeyPredicate(KThread thread, ulong address)
{
validThreads = WakeThreads(threads, count, TryAcquireMutex);
return thread.CondVarAddress == address;
}
int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address);
if (validThreads == 0)
{
KernelTransfer.KernelToUser(address, 0);
@ -329,24 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexAddress = address;
currentThread.WaitingInArbitration = true;
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
{
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]);
}
_arbiterThreads.Add(currentThread);
currentThread.Reschedule(ThreadSchedState.Paused);
if (timeout > 0)
@ -365,7 +336,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (currentThread.WaitingInArbitration)
{
_arbiterThreads[address].Remove(currentThread);
_arbiterThreads.Remove(currentThread);
currentThread.WaitingInArbitration = false;
}
@ -421,24 +392,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexAddress = address;
currentThread.WaitingInArbitration = true;
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
{
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]);
}
_arbiterThreads.Add(currentThread);
currentThread.Reschedule(ThreadSchedState.Paused);
if (timeout > 0)
@ -457,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (currentThread.WaitingInArbitration)
{
_arbiterThreads[address].Remove(currentThread);
_arbiterThreads.Remove(currentThread);
currentThread.WaitingInArbitration = false;
}
@ -530,12 +486,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// or equal to the Count of threads to be signaled, or Count is zero
// or negative. It is incremented if there are no threads waiting.
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)
{
@ -602,38 +561,55 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
thread.WaitingInArbitration = false;
}
_arbiterThreads.TryGetValue(address, out List<KThread> threads);
if (threads is not null && threads.Count > 0)
static bool ArbiterThreadPredecate(KThread thread, ulong address)
{
WakeThreads(threads, count, RemoveArbiterThread);
return thread.MutexAddress == address;
}
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address);
}
private static int WakeThreads(
List<KThread> threads,
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;
for (int i = 0; i < validCount; i++)
KThread[] candidates = _threadArrayPool.Allocate();
if (candidates.Length < threads.Count)
{
KThread thread = threads[i];
removeCallback(thread);
Array.Resize(ref candidates, threads.Count);
}
threads.RemoveRange(0, validCount);
return validCount;
}
private class ByDynamicPriority : IComparer<KThread>
{
public int Compare(KThread x, KThread y)
int validCount = 0;
for (int i = 0; i < threads.Count; i++)
{
return x!.DynamicPriority.CompareTo(y!.DynamicPriority);
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;
}
}
}

View File

@ -61,6 +61,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KSynchronizationObject SignaledObj { get; set; }
public ulong CondVarAddress { get; set; }
private ulong _entrypoint;
private ThreadStart _customThreadStart;
private bool _forcedUnschedulable;

View File

@ -416,7 +416,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.InvalidParameters;
}
context.Device.UIHandler.TakeScreenshot();
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { albumReportOption });
return ResultCode.Success;
}

View File

@ -23,9 +23,6 @@ namespace Ryujinx.HLE.HOS.Services
private int _selfId;
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)
{
Stopwatch sw = Stopwatch.StartNew();
@ -149,9 +146,7 @@ namespace Ryujinx.HLE.HOS.Services
{
Logger.Trace?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
_parameters[0] = context;
result = (ResultCode)processRequest.Invoke(service, _parameters);
result = (ResultCode)processRequest.Invoke(service, [context]);
}
else
{
@ -201,9 +196,7 @@ namespace Ryujinx.HLE.HOS.Services
{
Logger.Debug?.Print(LogClass.KernelIpc, $"{GetType().Name}: {processRequest.Name}");
_parameters[0] = context;
result = (ResultCode)processRequest.Invoke(this, _parameters);
result = (ResultCode)processRequest.Invoke(this, [context]);
}
else
{

View File

@ -59,10 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv
// TODO: This should call set:sys::GetDebugModeFlag
private readonly bool _debugModeEnabled = false;
private byte[] _ioctl2Buffer = [];
private byte[] _ioctlArgumentBuffer = [];
private byte[] _ioctl3Buffer = [];
public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer)
{
@ -132,38 +128,27 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments))
{
if (_ioctlArgumentBuffer.Length < (int)inputDataSize)
{
Array.Resize(ref _ioctlArgumentBuffer, (int)inputDataSize);
}
arguments = _ioctlArgumentBuffer.AsSpan(0, (int)inputDataSize);
arguments = new byte[inputDataSize];
context.Memory.Read(inputDataPosition, arguments);
}
else
{
arguments = arguments.ToArray();
}
}
else if (isWrite)
{
if (_ioctlArgumentBuffer.Length < (int)outputDataSize)
{
Array.Resize(ref _ioctlArgumentBuffer, (int)outputDataSize);
}
arguments = _ioctlArgumentBuffer.AsSpan(0, (int)outputDataSize);
byte[] outputData = new byte[outputDataSize];
arguments = new Span<byte>(outputData);
}
else
{
if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments))
{
if (_ioctlArgumentBuffer.Length < (int)inputDataSize)
{
Array.Resize(ref _ioctlArgumentBuffer, (int)inputDataSize);
}
arguments = _ioctlArgumentBuffer.AsSpan(0, (int)inputDataSize);
byte[] temp = new byte[inputDataSize];
context.Memory.Read(inputDataPosition, arguments);
}
context.Memory.Read(inputDataPosition, temp);
arguments = new Span<byte>(temp);
}
return NvResult.Success;
@ -285,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
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);
byte[] inlineInBuffer = null;
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBufferSpan))
{
if (_ioctl2Buffer.Length < (int)inlineInBufferSize)
{
Array.Resize(ref _ioctl2Buffer, (int)inlineInBufferSize);
}
inlineInBufferSpan = _ioctl2Buffer.AsSpan(0, (int)inlineInBufferSize);
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan);
inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize);
inlineInBufferSpan = inlineInBuffer;
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]);
}
if (errorCode == NvResult.Success)
@ -506,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan);
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]);
if (internalResult == NvInternalResult.NotImplemented)
{
@ -517,10 +500,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv
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);
@ -543,15 +531,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
byte[] inlineOutBuffer = null;
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBufferSpan))
{
if (_ioctl3Buffer.Length < (int)inlineOutBufferSize)
{
Array.Resize(ref _ioctl3Buffer, (int)inlineOutBufferSize);
}
inlineOutBufferSpan = _ioctl3Buffer.AsSpan(0, (int)inlineOutBufferSize);
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan);
inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize);
inlineOutBufferSpan = inlineOutBuffer;
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
}
if (errorCode == NvResult.Success)
@ -560,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan);
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
if (internalResult == NvInternalResult.NotImplemented)
{
@ -571,11 +557,16 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments);
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan);
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray());
}
}
}
if (inlineOutBuffer is not null)
{
_byteArrayPool.Return(inlineOutBuffer);
}
}
context.ResponseData.Write((uint)errorCode);

View File

@ -454,9 +454,8 @@ namespace Ryujinx.HLE.HOS.Services
response.RawData = _responseDataStream.ToArray();
RecyclableMemoryStream responseStream = response.GetStreamTipc();
using RecyclableMemoryStream responseStream = response.GetStreamTipc();
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
MemoryStreamManager.Shared.ReleaseStream(responseStream);
}
else
{
@ -465,9 +464,8 @@ namespace Ryujinx.HLE.HOS.Services
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());
MemoryStreamManager.Shared.ReleaseStream(responseStream);
}
return shouldReply;

View File

@ -68,10 +68,5 @@ namespace Ryujinx.HLE.UI
/// Displays the player select dialog and returns the selected profile.
/// </summary>
UserProfile ShowPlayerSelectDialog();
/// <summary>
/// Takes a screenshot from the current renderer and saves it in the screenshots folder.
/// </summary>
void TakeScreenshot();
}
}

View File

@ -19,9 +19,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
private int _waitingThreadHandle;
private MultiWaitHolderBase _signaledHolder;
ObjectPool<int[]> _objectHandlePool = new(() => new int[64]);
ObjectPool<MultiWaitHolderBase[]> _objectPool = new(() => new MultiWaitHolderBase[64]);
public long CurrentTime { get; private set; }
@ -79,15 +76,11 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout)
{
int[] objectHandles = _objectHandlePool.Allocate();
Span<int> objectHandlesSpan = objectHandles;
objectHandlesSpan.Clear();
Span<int> objectHandles = new int[64];
MultiWaitHolderBase[] objects = _objectPool.Allocate();
Span<MultiWaitHolderBase> objectsSpan = objects;
objectsSpan.Clear();
Span<MultiWaitHolderBase> objects = new MultiWaitHolderBase[64];
int count = FillObjectsArray(objectHandlesSpan, objectsSpan);
int count = FillObjectsArray(objectHandles, objects);
long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000;
@ -105,7 +98,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
}
else
{
index = WaitSynchronization(objectHandlesSpan[..count], minTimeout);
index = WaitSynchronization(objectHandles[..count], minTimeout);
DebugUtil.Assert(index != WaitInvalid);
}
@ -123,18 +116,12 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
{
_signaledHolder = minTimeoutObject;
_objectHandlePool.Release(objectHandles);
_objectPool.Release(objects);
return _signaledHolder;
}
}
}
else
{
_objectHandlePool.Release(objectHandles);
_objectPool.Release(objects);
return null;
}
@ -144,9 +131,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
{
if (_signaledHolder != null)
{
_objectHandlePool.Release(objectHandles);
_objectPool.Release(objects);
return _signaledHolder;
}
}
@ -155,11 +139,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
default:
lock (_lock)
{
_signaledHolder = objectsSpan[index];
_signaledHolder = objects[index];
_objectHandlePool.Release(objectHandles);
_objectPool.Release(objects);
return _signaledHolder;
}
}

View File

@ -532,6 +532,8 @@ namespace Ryujinx.Input.HLE
hidKeyboard.Modifier |= value << entry.Target;
}
ArrayPool<bool>.Shared.Return(keyboardState.KeysState);
return hidKeyboard;

View File

@ -20,6 +20,7 @@ namespace Ryujinx.Input.HLE
{
public class NpadManager : IDisposable
{
private static readonly ObjectPool<List<SixAxisInput>> _hleMotionStatesPool = new (() => new List<SixAxisInput>(NpadDevices.MaxControllers));
private readonly CemuHookClient _cemuHookClient;
private readonly Lock _lock = new();
@ -39,9 +40,6 @@ namespace Ryujinx.Input.HLE
private bool _enableKeyboard;
private bool _enableMouse;
private Switch _device;
private readonly List<GamepadInput> _hleInputStates = [];
private readonly List<SixAxisInput> _hleMotionStates = new(NpadDevices.MaxControllers);
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver)
{
@ -219,8 +217,8 @@ namespace Ryujinx.Input.HLE
{
lock (_lock)
{
_hleInputStates.Clear();
_hleMotionStates.Clear();
List<GamepadInput> hleInputStates = [];
List<SixAxisInput> hleMotionStates = _hleMotionStatesPool.Allocate();
KeyboardInput? hleKeyboardInput = null;
@ -262,14 +260,14 @@ namespace Ryujinx.Input.HLE
inputState.PlayerId = playerIndex;
motionState.Item1.PlayerId = playerIndex;
_hleInputStates.Add(inputState);
_hleMotionStates.Add(motionState.Item1);
hleInputStates.Add(inputState);
hleMotionStates.Add(motionState.Item1);
if (isJoyconPair && !motionState.Item2.Equals(default))
{
motionState.Item2.PlayerId = playerIndex;
_hleMotionStates.Add(motionState.Item2);
hleMotionStates.Add(motionState.Item2);
}
}
@ -278,8 +276,8 @@ namespace Ryujinx.Input.HLE
hleKeyboardInput = NpadController.GetHLEKeyboardInput(_keyboardDriver);
}
_device.Hid.Npads.Update(_hleInputStates);
_device.Hid.Npads.UpdateSixAxis(_hleMotionStates);
_device.Hid.Npads.Update(hleInputStates);
_device.Hid.Npads.UpdateSixAxis(hleMotionStates);
if (hleKeyboardInput.HasValue)
{
@ -330,7 +328,10 @@ namespace Ryujinx.Input.HLE
_device.Hid.Mouse.Update(0, 0);
}
_device.TamperMachine.UpdateInput(_hleInputStates);
_device.TamperMachine.UpdateInput(hleInputStates);
hleMotionStates.Clear();
_hleMotionStatesPool.Release(hleMotionStates);
}
}

View File

@ -8,8 +8,6 @@ namespace Ryujinx.Input
/// </summary>
public interface IKeyboard : IGamepad
{
private static bool[] _keyState;
/// <summary>
/// Check if a given key is pressed on the keyboard.
/// </summary>
@ -31,17 +29,15 @@ namespace Ryujinx.Input
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
{
if (_keyState is null)
{
_keyState = new bool[(int)Key.Count];
}
bool[] keysState = ArrayPool<bool>.Shared.Rent((int)Key.Count);
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);
}
}
}

View File

@ -3,7 +3,7 @@ namespace Ryujinx.Memory.Range
/// <summary>
/// Range of memory that can be split in two.
/// </summary>
public interface INonOverlappingRange<T> : IRangeListRange<T> where T : class, IRangeListRange<T>
public interface INonOverlappingRange : IRange
{
/// <summary>
/// Split this region into two, around the specified address.
@ -11,6 +11,6 @@ namespace Ryujinx.Memory.Range
/// </summary>
/// <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>
public INonOverlappingRange<T> Split(ulong splitAddress);
public INonOverlappingRange Split(ulong splitAddress);
}
}

View File

@ -24,8 +24,8 @@ namespace Ryujinx.Memory.Range
/// Check if this range overlaps with another.
/// </summary>
/// <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>
bool OverlapsWith(ulong address, ulong endAddress);
bool OverlapsWith(ulong address, ulong size);
}
}

View File

@ -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.
/// </summary>
/// <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();
@ -32,18 +32,83 @@ namespace Ryujinx.Memory.Range
/// <param name="item">The item to be added</param>
public override void Add(T item)
{
Debug.Assert(item.Address != item.EndAddress);
int index = BinarySearch(item.Address);
if (index < 0)
{
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)
{
Array.Resize(ref Items, (int)(Items.Length * 1.5));
Array.Resize(ref Items, Items.Length + BackingGrowthSize);
}
if (index >= Count)
@ -80,6 +145,8 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveAt(int index)
{
_rangeItemPool.Release(Items[index]);
if (index < Count - 1)
{
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
@ -106,7 +173,7 @@ namespace Ryujinx.Memory.Range
{
int index = BinarySearch(item.Address);
if (index >= 0 && Items[index] == item)
if (index >= 0 && Items[index].Value.Equals(item))
{
RemoveAt(index);
@ -121,7 +188,7 @@ namespace Ryujinx.Memory.Range
/// </summary>
/// <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>
public override void RemoveRange(T startItem, T endItem)
public override void RemoveRange(RangeItem<T> startItem, RangeItem<T> endItem)
{
if (startItem is null)
{
@ -130,7 +197,7 @@ namespace Ryujinx.Memory.Range
if (startItem == endItem)
{
Remove(startItem);
Remove(startItem.Value);
return;
}
@ -162,45 +229,42 @@ namespace Ryujinx.Memory.Range
/// <param name="size">Size of the range</param>
public void RemoveRange(ulong address, ulong size)
{
(int startIndex, int endIndex) = BinarySearchEdges(address, address + size);
int startIndex = BinarySearchLeftEdge(address, address + size);
if (startIndex < 0)
{
return;
}
if (startIndex == endIndex - 1)
int endIndex = startIndex;
while (Items[endIndex] is not null && Items[endIndex].Address < address + size)
{
RemoveAt(startIndex);
return;
if (endIndex == Count - 1)
{
break;
}
endIndex++;
}
RemoveRangeInternal(startIndex, endIndex);
}
/// <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)
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>
@ -232,8 +296,8 @@ namespace Ryujinx.Memory.Range
// So we need to return both the split 0-1 and 1-2 ranges.
Lock.EnterWriteLock();
(T first, T last) = FindOverlapsAsNodes(address, size);
list = [];
(RangeItem<T> first, RangeItem<T> last) = FindOverlapsAsNodes(address, size);
list = new List<T>();
if (first is null)
{
@ -247,41 +311,42 @@ namespace Ryujinx.Memory.Range
ulong lastAddress = address;
ulong endAddress = address + size;
T current = first;
RangeItem<T> current = first;
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.
list.Add(current);
list.Add(region);
Lock.ExitWriteLock();
return;
}
if (lastAddress < current.Address)
if (lastAddress < region.Address)
{
// 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);
Add(fillRegion);
}
if (current.Address < address)
if (region.Address < address)
{
// 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(current, address + size);
Split(region, address + size);
}
list.Add(current);
lastAddress = current.EndAddress;
list.Add(region);
lastAddress = region.EndAddress;
current = current.Next;
}
@ -309,6 +374,7 @@ namespace Ryujinx.Memory.Range
private T Split(T region, ulong splitAddress)
{
T newRegion = (T)region.Split(splitAddress);
Update(region);
Add(newRegion);
return newRegion;
}
@ -320,11 +386,16 @@ namespace Ryujinx.Memory.Range
/// <param name="size">Size in bytes of the range</param>
/// <returns>The leftmost overlapping item, or null if none is found</returns>
[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);
return index < 0 ? null : Items[index];
if (index < 0)
{
return null;
}
return Items[index];
}
/// <summary>
@ -334,11 +405,16 @@ namespace Ryujinx.Memory.Range
/// <param name="size">Size in bytes of the range</param>
/// <returns>The overlapping item, or null if none is found</returns>
[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);
return index < 0 ? null : Items[index];
if (index < 0)
{
return null;
}
return Items[index];
}
/// <summary>
@ -348,18 +424,23 @@ namespace Ryujinx.Memory.Range
/// <param name="size">Size in bytes of the range</param>
/// <returns>The first and last overlapping items, or null if none are found</returns>
[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);
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);
T[] result;
RangeItem<T>[] result;
if (index < 0)
{
@ -368,20 +449,29 @@ namespace Ryujinx.Memory.Range
}
else
{
result = ArrayPool<T>.Shared.Rent(endIndex - index);
result = ArrayPool<RangeItem<T>>.Shared.Rent(endIndex - index);
length = endIndex - index;
Items.AsSpan(index, endIndex - index).CopyTo(result);
Array.Copy(Items, index, result, 0, endIndex - index);
}
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);
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;
}
@ -390,7 +480,7 @@ namespace Ryujinx.Memory.Range
{
for (int i = 0; i < Count; i++)
{
yield return Items[i];
yield return Items[i].Value;
}
}
}

View File

@ -14,14 +14,14 @@ namespace Ryujinx.Memory.Range
/// startIndex is inclusive.
/// endIndex is exclusive.
/// </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 EndIndex = -1;
public readonly T QuickResult;
public readonly RangeItem<T> QuickResult;
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.EndIndex = endIndex;
@ -33,7 +33,7 @@ namespace Ryujinx.Memory.Range
/// Sorted list of ranges that supports binary search.
/// </summary>
/// <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();
@ -61,6 +61,104 @@ namespace Ryujinx.Memory.Range
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)
{
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="endItem">The last item in the range of items to be removed</param>
[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)
{
@ -131,29 +229,30 @@ namespace Ryujinx.Memory.Range
if (startItem == endItem)
{
Remove(startItem);
Remove(startItem.Value);
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>
@ -169,7 +268,7 @@ namespace Ryujinx.Memory.Range
{
while (index < Count)
{
if (Items[index] == item)
if (Items[index].Value.Equals(item))
{
RemoveAt(index);
@ -199,7 +298,7 @@ namespace Ryujinx.Memory.Range
/// <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>
[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);
@ -222,7 +321,7 @@ namespace Ryujinx.Memory.Range
/// <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>
[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);
@ -241,7 +340,7 @@ namespace Ryujinx.Memory.Range
/// <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>
/// <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;
@ -254,7 +353,7 @@ namespace Ryujinx.Memory.Range
for (int i = startIndex; i < Count; i++)
{
T item = Items[i];
ref RangeItem<T> item = ref Items[i];
if (item.Address >= endAddress)
{
@ -299,7 +398,7 @@ namespace Ryujinx.Memory.Range
{
for (int i = 0; i < Count; i++)
{
yield return Items[i];
yield return Items[i].Value;
}
}
}

View File

@ -1,22 +1,56 @@
using Ryujinx.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Ryujinx.Memory.Range
{
public interface IRangeListRange<TValue> : IRange where TValue : class, IRangeListRange<TValue>
public class RangeItem<TValue> where TValue : IRange
{
public TValue Next { get; set; }
public TValue Previous { get; set; }
public RangeItem<TValue> Next;
public RangeItem<TValue> Previous;
public ulong Address;
public ulong EndAddress;
public TValue Value;
public RangeItem()
{
}
public RangeItem(TValue value)
{
Address = value.Address;
EndAddress = value.Address + value.Size;
Value = value;
}
public RangeItem<TValue> Set(TValue value)
{
Next = null;
Previous = null;
Address = value.Address;
EndAddress = value.Address + value.Size;
Value = value;
return this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool OverlapsWith(ulong address, ulong endAddress)
{
return Address < endAddress && address < EndAddress;
}
}
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : class, IRangeListRange<T>
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
{
protected static readonly ObjectPool<RangeItem<T>> _rangeItemPool = new(() => new RangeItem<T>());
private const int BackingInitialSize = 1024;
protected T[] Items;
protected RangeItem<T>[] Items;
protected readonly int BackingGrowthSize;
public int Count { get; protected set; }
@ -28,18 +62,32 @@ namespace Ryujinx.Memory.Range
protected RangeListBase(int backingInitialSize = BackingInitialSize)
{
BackingGrowthSize = backingInitialSize;
Items = new T[backingInitialSize];
Items = new RangeItem<T>[backingInitialSize];
}
public abstract void Add(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 abstract bool Update(T item);
/// <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 abstract bool Update(RangeItem<T> item);
public abstract bool Remove(T item);
public abstract void RemoveRange(T startItem, T endItem);
public abstract void RemoveRange(RangeItem<T> startItem, RangeItem<T> endItem);
public abstract T FindOverlap(ulong address, ulong size);
public abstract RangeItem<T> FindOverlap(ulong address, ulong size);
public abstract T FindOverlapFast(ulong address, ulong size);
public abstract RangeItem<T> FindOverlapFast(ulong address, ulong size);
/// <summary>
/// Performs binary search on the internal list of items.
@ -58,7 +106,7 @@ namespace Ryujinx.Memory.Range
int middle = left + (range >> 1);
T item = Items[middle];
ref RangeItem<T> item = ref Items[middle];
if (item.Address == address)
{
@ -96,7 +144,7 @@ namespace Ryujinx.Memory.Range
int middle = left + (range >> 1);
T item = Items[middle];
ref RangeItem<T> item = ref Items[middle];
if (item.OverlapsWith(address, endAddress))
{
@ -137,7 +185,7 @@ namespace Ryujinx.Memory.Range
int middle = left + (range >> 1);
T item = Items[middle];
ref RangeItem<T> item = ref Items[middle];
bool match = item.OverlapsWith(address, endAddress);
@ -189,7 +237,7 @@ namespace Ryujinx.Memory.Range
int middle = right - (range >> 1);
T item = Items[middle];
ref RangeItem<T> item = ref Items[middle];
bool match = item.OverlapsWith(address, endAddress);
@ -234,7 +282,7 @@ namespace Ryujinx.Memory.Range
if (Count == 1)
{
T item = Items[0];
ref RangeItem<T> item = ref Items[0];
if (item.OverlapsWith(address, endAddress))
{
@ -264,7 +312,7 @@ namespace Ryujinx.Memory.Range
int middle = left + (range >> 1);
T item = Items[middle];
ref RangeItem<T> item = ref Items[middle];
bool match = item.OverlapsWith(address, endAddress);
@ -321,7 +369,7 @@ namespace Ryujinx.Memory.Range
int middle = right - (range >> 1);
T item = Items[middle];
ref RangeItem<T> item = ref Items[middle];
bool match = item.OverlapsWith(address, endAddress);

View File

@ -5,7 +5,7 @@ namespace Ryujinx.Memory.Tracking
/// <summary>
/// A region of memory.
/// </summary>
abstract class AbstractRegion<T> : INonOverlappingRange<T> where T : class, INonOverlappingRange<T>
abstract class AbstractRegion : INonOverlappingRange
{
/// <summary>
/// Base address.
@ -21,9 +21,6 @@ namespace Ryujinx.Memory.Tracking
/// End address.
/// </summary>
public ulong EndAddress => Address + Size;
public T Next { get; set; }
public T Previous { get; set; }
/// <summary>
/// Create a new region.
@ -40,11 +37,11 @@ namespace Ryujinx.Memory.Tracking
/// Check if this range overlaps with another.
/// </summary>
/// <param name="address">Base address</param>
/// <param name="endAddress">End address</param>
/// <param name="size">Size of the range</param>
/// <returns>True if overlapping, 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;
}
/// <summary>
@ -71,6 +68,6 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
/// <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>
public abstract INonOverlappingRange<T> Split(ulong splitAddress);
public abstract INonOverlappingRange Split(ulong splitAddress);
}
}

View File

@ -81,10 +81,10 @@ namespace Ryujinx.Memory.Tracking
{
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
regions.Lock.EnterReadLock();
ReadOnlySpan<VirtualRegion> overlaps = regions.FindOverlapsAsSpan(va, size);
Span<RangeItem<VirtualRegion>> overlaps = regions.FindOverlapsAsSpan(va, size);
for (int i = 0; i < overlaps.Length; i++)
{
VirtualRegion region = overlaps[i];
VirtualRegion region = overlaps[i].Value;
// If the region has been fully remapped, signal that it has been mapped again.
bool remapped = _memoryManager.IsRangeMapped(region.Address, region.Size);
@ -117,11 +117,11 @@ namespace Ryujinx.Memory.Tracking
{
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
regions.Lock.EnterReadLock();
ReadOnlySpan<VirtualRegion> overlaps = regions.FindOverlapsAsSpan(va, size);
Span<RangeItem<VirtualRegion>> overlaps = regions.FindOverlapsAsSpan(va, size);
for (int i = 0; i < overlaps.Length; i++)
{
overlaps[i].SignalMappingChanged(false);
overlaps[i].Value.SignalMappingChanged(false);
}
regions.Lock.ExitReadLock();
}
@ -301,7 +301,7 @@ namespace Ryujinx.Memory.Tracking
// We use the non-span method here because keeping the lock will cause a deadlock.
regions.Lock.EnterReadLock();
VirtualRegion[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
regions.Lock.ExitReadLock();
if (length == 0 && !precise)
@ -327,7 +327,7 @@ namespace Ryujinx.Memory.Tracking
for (int i = 0; i < length; i++)
{
VirtualRegion region = overlaps[i];
VirtualRegion region = overlaps[i].Value;
if (precise)
{
@ -341,7 +341,7 @@ namespace Ryujinx.Memory.Tracking
if (length != 0)
{
ArrayPool<VirtualRegion>.Shared.Return(overlaps);
ArrayPool<RangeItem<VirtualRegion>>.Shared.Return(overlaps);
}
}
}

View File

@ -6,7 +6,7 @@ namespace Ryujinx.Memory.Tracking
/// <summary>
/// A region of virtual memory.
/// </summary>
class VirtualRegion : AbstractRegion<VirtualRegion>
class VirtualRegion : AbstractRegion
{
public List<RegionHandle> Handles = [];
@ -137,7 +137,7 @@ namespace Ryujinx.Memory.Tracking
}
}
public override INonOverlappingRange<VirtualRegion> Split(ulong splitAddress)
public override INonOverlappingRange Split(ulong splitAddress)
{
VirtualRegion newRegion = new(_tracking, splitAddress, EndAddress - splitAddress, Guest, _lastPermission);
Size = splitAddress - Address;

View File

@ -580,10 +580,5 @@ namespace Ryujinx.Headless
{
return AccountSaveDataManager.GetLastUsedUser();
}
public void TakeScreenshot()
{
throw new NotImplementedException();
}
}
}

View File

@ -17,7 +17,6 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Headless;
using Ryujinx.SDL3.Common;
@ -47,7 +46,7 @@ namespace Ryujinx.Ava
public static int Main(string[] args)
{
Version = ReleaseInformation.Version;
if (OperatingSystem.IsWindows())
{
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
@ -56,11 +55,8 @@ namespace Ryujinx.Ava
return 0;
}
var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
if (Environment.CurrentDirectory.StartsWithIgnoreCase(programFiles) ||
Environment.CurrentDirectory.StartsWithIgnoreCase(programFilesX86))
if (Environment.CurrentDirectory.StartsWithIgnoreCase("C:\\Program Files") ||
Environment.CurrentDirectory.StartsWithIgnoreCase("C:\\Program Files (x86)"))
{
_ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run from the Program Files folder. Please move it out and relaunch.", $"Ryujinx {Version}", MbIconwarning);
return 0;
@ -77,23 +73,11 @@ namespace Ryujinx.Ava
}
}
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
// This is undesirable and causes very odd behavior during development (the process stops responding,
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.
// This needs to be investigated, but calling prctl() is better than modifying system-wide settings or leaving this be.
if (!coreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
PreviewerDetached = true;
if (noGuiArg)
if (args.Length > 0 && args[0] is "--no-gui" or "nogui")
{
HeadlessRyujinx.Entrypoint(args);
HeadlessRyujinx.Entrypoint(args[1..]);
return 0;
}
@ -128,14 +112,6 @@ namespace Ryujinx.Ava
: [Win32RenderingMode.Software]
});
private static bool ConsumeCommandLineArgument(ref string[] args, string targetArgument)
{
List<string> argList = [.. args];
bool found = argList.Remove(targetArgument);
args = argList.ToArray();
return found;
}
private static void Initialize(string[] args)
{
// Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched
@ -201,6 +177,7 @@ namespace Ryujinx.Ava
}
}
public static string GetDirGameUserConfig(string gameId, bool changeFolderForGame = false)
{
if (string.IsNullOrEmpty(gameId))

View File

@ -327,10 +327,5 @@ namespace Ryujinx.Ava.UI.Applet
return profile;
}
public void TakeScreenshot()
{
_parent.ViewModel.AppHost.ScreenshotRequested = true;
}
}
}

View File

@ -1333,10 +1333,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public void TakeScreenshot()
{
AppHost.ScreenshotRequested = true;
}
public void TakeScreenshot() => AppHost.ScreenshotRequested = true;
public void HideUi() => ShowMenuAndStatusBar = false;