Compare commits

..

20 Commits

Author SHA1 Message Date
GreemDev
740c346b3b add temporary logs 2025-06-03 18:19:27 -05:00
GreemDev
c9116e2a52 use changelog url format variable in more places + remove stable url format variable 2025-06-03 18:17:02 -05:00
GreemDev
91248bdd32 Remove inner try on Updater.CheckGitLabVersionAsync & apply formatting to file 2025-06-03 17:56:11 -05:00
GreemDev
64ecd514f2 Add long request time tolerance 2025-06-03 17:44:36 -05:00
GreemDev
8aabdd8714 make package name look nicer 2025-06-03 17:34:30 -05:00
GreemDev
1bf31c91b6 checkout code before trying to get the current repo's commit lol 2025-06-03 04:56:13 -05:00
GreemDev
bf5326ec87 installing it into path before using it is causing issues in stable pipeline but not canary for some reason 2025-06-03 04:45:11 -05:00
GreemDev
6e1e081d90 fix mac stable file path 2025-06-03 04:19:25 -05:00
GreemDev
8b58416003 provide github token env var 2025-06-03 04:15:19 -05:00
GreemDev
24f2d91800 Merge branch 'master' into gitlab-releases/part1 2025-06-03 04:07:11 -05:00
GreemDev
783b761745 i think this needs to be on the main branch before i can run it 2025-06-03 04:03:33 -05:00
GreemDev
559d9b4db0 stable release test workflow 2025-06-03 04:00:06 -05:00
GreemDev
787a14ed2b move version checking into a single method to unify updater logic across the app 2025-06-03 03:09:50 -05:00
GreemDev
d0c055ccb8 fix fallback url for stable changelogs being the current release channel 2025-06-03 02:58:01 -05:00
GreemDev
72935bdbb6 forgot some things & remove redundant method on github release channel 2025-06-03 02:55:25 -05:00
GreemDev
0df51e24dd initial impl of gitlab updating
should be version agnostic
2025-06-03 02:48:18 -05:00
GreemDev
4b003498a3 wrong command enum name 2025-06-02 22:34:50 -05:00
GreemDev
23b2576ded fix mac AGAIN 2025-06-02 22:12:53 -05:00
GreemDev
3f653626c8 attempt 2 (yeah downloading a linux binary on windows won't work who could have guessed (certainly not I!)) 2025-06-02 22:03:14 -05:00
GreemDev
052510ae1a Add GitLab package publishing to canary script 2025-06-02 21:50:10 -05:00
253 changed files with 6848 additions and 12985 deletions

View File

@@ -24,6 +24,54 @@ env:
RELEASE: 1 RELEASE: 1
jobs: jobs:
tag:
name: Create tag
runs-on: ubuntu-24.04
steps:
- 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
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 GitLab tag
run: gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateTag "Canary-${{ steps.version_info.outputs.build_version }}|master"
- name: Create release
uses: ncipollo/release-action@v1
with:
name: "Canary ${{ steps.version_info.outputs.build_version }}"
tag: ${{ steps.version_info.outputs.build_version }}
body: |
# Canary builds:
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/latest) instead if that sounds like something you don't want to deal with.
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Canary Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})**
omitBodyDuringUpdate: true
owner: ${{ secrets.RC_OWNER }}
repo: ${{ secrets.RC_CANARY_NAME }}
token: ${{ secrets.ALT_RELEASE_TOKEN }}
release: release:
name: Release for ${{ matrix.platform.name }} name: Release for ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
@@ -44,6 +92,16 @@ jobs:
- name: Overwrite csc problem matcher - name: Overwrite csc problem matcher
run: echo "::add-matcher::.github/csc.json" run: echo "::add-matcher::.github/csc.json"
- name: Install 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: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
@@ -146,6 +204,33 @@ jobs:
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.zsync" 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.zsync"
shell: bash shell: bash
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
tag: ${{ steps.version_info.outputs.build_version }}
body: |
# Canary builds:
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/latest) instead if that sounds like something you don't want to deal with.
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Canary Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})**
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true
owner: ${{ secrets.RC_OWNER }}
repo: ${{ secrets.RC_CANARY_NAME }}
token: ${{ secrets.ALT_RELEASE_TOKEN }}
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
@@ -205,15 +290,28 @@ jobs:
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1 ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
gli --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" 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"
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: "Canary ${{ steps.version_info.outputs.build_version }}"
artifacts: "publish_ava/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }}
body: ""
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true
owner: ${{ secrets.RC_OWNER }}
repo: ${{ secrets.RC_CANARY_NAME }}
token: ${{ secrets.ALT_RELEASE_TOKEN }}
create_gitlab_release: create_gitlab_release:
name: Create GitLab Release name: Create GitLab Release
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- tag
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v4
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
@@ -232,18 +330,6 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create tag
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateTag "Canary-${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}"
- name: Create release - name: Create release
run: | run: |
gli --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 }})" 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](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 --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: |
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=canary' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'

View File

@@ -14,6 +14,38 @@ env:
RELEASE: 1 RELEASE: 1
jobs: jobs:
tag:
name: Create tag
runs-on: ubuntu-24.04
steps:
- 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
shell: bash
- name: Create release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
tag: ${{ steps.version_info.outputs.build_version }}
body: |
# Stable builds:
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Stable Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Stable macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }})**
omitBodyDuringUpdate: true
owner: ${{ secrets.RC_OWNER }}
repo: ${{ secrets.RC_STABLE_NAME }}
token: ${{ secrets.ALT_RELEASE_TOKEN }}
release: release:
name: Release for ${{ matrix.platform.name }} name: Release for ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
@@ -137,6 +169,30 @@ jobs:
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" 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 shell: bash
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
tag: ${{ steps.version_info.outputs.build_version }}
body: |
# Stable builds:
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Stable Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Stable macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }})**
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true
owner: ${{ secrets.RC_OWNER }}
repo: ${{ secrets.RC_STABLE_NAME }}
token: ${{ secrets.ALT_RELEASE_TOKEN }}
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
@@ -194,10 +250,25 @@ jobs:
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0 ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz" gli --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"
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "publish/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }}
body: ""
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true
owner: ${{ secrets.RC_OWNER }}
repo: ${{ secrets.RC_STABLE_NAME }}
token: ${{ secrets.ALT_RELEASE_TOKEN }}
create_gitlab_release: create_gitlab_release:
name: Create GitLab Release name: Create GitLab Release
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- tag
- macos_release - macos_release
- release - release
steps: steps:
@@ -223,12 +294,4 @@ jobs:
- name: Create release - name: Create release
run: | 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 }}|${{ steps.version_info.outputs.build_version }}|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 }}|**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }})**"
- name: Send notification webhook
run: |
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: |
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=stable' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'

18
BuildAndPushLibraries.sh Normal file
View File

@@ -0,0 +1,18 @@
function pub {
dotnet publish -c release
}
function package {
cd src/$1
pub
mv bin/Release/$1.1.0.0.nupkg ../../pkgs/$1.1.0.0.nupkg
cd ../../
}
rm -rf pkgs
mkdir pkgs
package ARMeilleure
package Ryujinx.Memory
dotnet nuget push pkgs/*.nupkg --source RyubingPkgs

View File

@@ -2,17 +2,20 @@
All updates to this Ryujinx branch will be documented in this file. All updates to this Ryujinx branch will be documented in this file.
## [1.3.2](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.3.2>) - 2025-06-09
## [1.3.1](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.3.1>) - 2025-04-23 ## [1.3.1](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.3.1>) - 2025-04-23
A list of notable changes can be found on the release linked in the version number above.
## [1.2.86](<https://github.com/Ryubing/Stable-Releases/releases/tag/1.2.86>) - 2025-03-13 ## [1.2.86](<https://github.com/Ryubing/Stable-Releases/releases/tag/1.2.86>) - 2025-03-13
A list of notable changes can be found on the release linked in the version number above.
## [1.2.82](<https://web.archive.org/web/20250312010534/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.82>) - 2025-02-16 ## [1.2.82](<https://web.archive.org/web/20250312010534/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.82>) - 2025-02-16
A list of notable changes can be found on the release linked in the version number above.
## [1.2.80-81](<https://web.archive.org/web/20250302064257/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.81>) - 2025-01-22 ## [1.2.80-81](<https://web.archive.org/web/20250302064257/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.81>) - 2025-01-22
A list of notable changes can be found on the release linked in the version number above.
## [1.2.78](<https://web.archive.org/web/20250301174537/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.78>) - 2024-12-19 ## [1.2.78](<https://web.archive.org/web/20250301174537/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.78>) - 2024-12-19
A list of notable changes can be found on the release linked in the version number above.
## [1.2.73-1.2.76](<https://web.archive.org/web/20250209202612/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.76>) - 2024-11-19 ## [1.2.73-1.2.76](<https://web.archive.org/web/20250209202612/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.76>) - 2024-11-19
A list of notable changes can be found on the release linked in the version number above. A list of notable changes can be found on the release linked in the version number above.
@@ -21,8 +24,8 @@ Additionally, 1.2.74 & 75 were fixes for uploading Windows build artifacts.
1.2.76 fixes a rare crash on startup. 1.2.76 fixes a rare crash on startup.
## [1.2.72](<https://git.ryujinx.app/ryubing/ryujinx/-/tags/1.2.72>) - 2024-11-03 ## [1.2.72](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.72>) - 2024-11-03
PRs [#163](<https://web.archive.org/web/20241123015123/https://github.com/GreemDev/Ryujinx/pull/163>), [#164](<https://web.archive.org/web/20250307192526/https://github.com/Ryubing/Ryujinx/pull/164>), [#139](<https://web.archive.org/web/20250306123457/https://github.com/Ryubing/Ryujinx/pull/139>) PRs [#163](<https://github.com/GreemDev/Ryujinx/pull/163>), [#164](<https://github.com/GreemDev/Ryujinx/pull/164>), [#139](<https://github.com/GreemDev/Ryujinx/pull/139>)
### HLE: ### HLE:
- Add DebugMouse HID device. - Add DebugMouse HID device.
- Fixes "Clock Tower Rewind" crashing while loading. - Fixes "Clock Tower Rewind" crashing while loading.
@@ -32,7 +35,7 @@ PRs [#163](<https://web.archive.org/web/20241123015123/https://github.com/GreemD
### misc: ### misc:
- Update macOS distribution .icns. - Update macOS distribution .icns.
## [1.2.69](<https://git.ryujinx.app/ryubing/ryujinx/-/tags/1.2.69>) - 2024-11-01 ## [1.2.69](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.69>) - 2024-11-01
### Infra: ### Infra:
- Compile the native libraries into the Ryujinx executable. - Compile the native libraries into the Ryujinx executable.
- Remove `libarmeilleure-jitsupport.dylib` from Windows & Linux releases (dylibs are macOS-only) - Remove `libarmeilleure-jitsupport.dylib` from Windows & Linux releases (dylibs are macOS-only)
@@ -42,8 +45,8 @@ PRs [#163](<https://web.archive.org/web/20241123015123/https://github.com/GreemD
- Replace "" with `string.Empty`. - Replace "" with `string.Empty`.
- Code cleanups & simplifications. - Code cleanups & simplifications.
## [1.2.67](<https://git.ryujinx.app/ryubing/ryujinx/-/tags/1.2.67>) - 2024-11-01 ## [1.2.67](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.67>) - 2024-11-01
PRs [#36](<https://web.archive.org/web/20250306215917/https://github.com/Ryubing/Ryujinx/pull/36>), [#135](<https://web.archive.org/web/20241122135125/https://github.com/GreemDev/Ryujinx/pull/135>) PRs [#36](<https://github.com/GreemDev/Ryujinx/pull/36>), [#135](<https://github.com/GreemDev/Ryujinx/pull/135>)
### GUI: ### GUI:
- Set UseFloatingWatermark to false when watermark is empty - Set UseFloatingWatermark to false when watermark is empty
@@ -54,8 +57,8 @@ PRs [#36](<https://web.archive.org/web/20250306215917/https://github.com/Ryubing
- Fix homebrew loading. - Fix homebrew loading.
## [1.2.64](https://git.ryujinx.app/ryubing/ryujinx/-/tags/1.2.64) - 2024-10-30 ## [1.2.64](https://github.com/GreemDev/Ryujinx/releases/tag/1.2.64) - 2024-10-30
PRs [#92](https://web.archive.org/web/20241118052724/https://github.com/GreemDev/Ryujinx/pull/92), ~~[#96](https://github.com/GreemDev/Ryujinx/pull/96)~~, ~~[#97](https://github.com/GreemDev/Ryujinx/pull/97)~~, [#101](https://web.archive.org/web/20250306223605/https://github.com/Ryubing/Ryujinx/pull/101), ~~[#103](https://github.com/GreemDev/Ryujinx/pull/103)~~ PRs [#92](https://github.com/GreemDev/Ryujinx/pull/92), [#96](https://github.com/GreemDev/Ryujinx/pull/96), [#97](https://github.com/GreemDev/Ryujinx/pull/97), [#101](https://github.com/GreemDev/Ryujinx/pull/101), [#103](https://github.com/GreemDev/Ryujinx/pull/103)
### GUI: ### GUI:
- Option to show classic-style title bar. Requires restart of emulator to take effect. - Option to show classic-style title bar. Requires restart of emulator to take effect.
- This is only relevant on Windows. Other Operating Systems default to this being on and not being changeable, because the custom (current) title bar only works on Windows in the first place. - This is only relevant on Windows. Other Operating Systems default to this being on and not being changeable, because the custom (current) title bar only works on Windows in the first place.
@@ -71,14 +74,14 @@ PRs [#92](https://web.archive.org/web/20241118052724/https://github.com/GreemDev
## 1.2.59 - 2024-10-27 ## 1.2.59 - 2024-10-27
PRs ~~[#88](https://github.com/GreemDev/Ryujinx/pull/88), [#87](https://github.com/GreemDev/Ryujinx/pull/87)~~ PRs [#88](https://github.com/GreemDev/Ryujinx/pull/88), [#87](https://github.com/GreemDev/Ryujinx/pull/87)
### i18n: ### i18n:
- fr_FR: - fr_FR:
- Add missing translations for new features & fix a couple wrong ones. - Add missing translations for new features & fix a couple wrong ones.
- Fix Ignore Missing Services / Ignore Applet tooltip. - Fix Ignore Missing Services / Ignore Applet tooltip.
## 1.2.57 - 2024-10-27 ## 1.2.57 - 2024-10-27
PRs ~~[#60](https://github.com/GreemDev/Ryujinx/pull/60)~~, [#42](https://web.archive.org/web/20241126203614/https://github.com/GreemDev/Ryujinx/pull/42) PRs [#60](https://github.com/GreemDev/Ryujinx/pull/60), [#42](https://github.com/GreemDev/Ryujinx/pull/42)
### GUI: ### GUI:
- Automatically remove invalid DLC & updates as part of autoload. - Automatically remove invalid DLC & updates as part of autoload.
- Added Thai translation for Ignore Applet hover tooltip. - Added Thai translation for Ignore Applet hover tooltip.
@@ -104,7 +107,7 @@ PRs ~~[#60](https://github.com/GreemDev/Ryujinx/pull/60)~~, [#42](https://web.ar
- Code cleanup. - Code cleanup.
## 1.2.44 - 2024-10-25 ## 1.2.44 - 2024-10-25
PR [#59](https://web.archive.org/web/20241125060420/https://github.com/GreemDev/Ryujinx/pull/59) PR [#59](https://github.com/GreemDev/Ryujinx/pull/59)
### GUI: ### GUI:
- Add descriptions for "ignoring applet" translated into other languages. - Add descriptions for "ignoring applet" translated into other languages.
@@ -117,9 +120,9 @@ NOTE: The translation isn't referenced in the code yet, it will be in the next u
## 1.2.42 - 2024-10-24 ## 1.2.42 - 2024-10-24
Sources: Sources:
Init function: [archive of github.com/MutantAura/Ryujinx/commit/9cef4ceba40d66492ff775af793ff70e6e7551a9](https://web.archive.org/web/20241122193401/https://github.com/MutantAura/Ryujinx/commit/9cef4ceba40d66492ff775af793ff70e6e7551a9) Init function: https://github.com/MutantAura/Ryujinx/commit/9cef4ceba40d66492ff775af793ff70e6e7551a9
Shader counter: ~~https://github.com/MutantAura/Ryujinx/commit/67b873645fd593e83d042a77bf7ab12e5ec97357~~ Original commit has been lost Shader counter: https://github.com/MutantAura/Ryujinx/commit/67b873645fd593e83d042a77bf7ab12e5ec97357
Thanks MutantAura :D Thanks MutantAura :D
### GUI: ### GUI:
@@ -127,14 +130,14 @@ Thanks MutantAura :D
- Remove graphics backend / GPU name event logic in favor of a single init function. - Remove graphics backend / GPU name event logic in favor of a single init function.
## 1.2.41 - 2024-10-24 ## 1.2.41 - 2024-10-24
PR ~~[#54](https://github.com/GreemDev/Ryujinx/pull/54)~~ PR [#54](https://github.com/GreemDev/Ryujinx/pull/54)
Thanks Whitescatz! Thanks Whitescatz!
### i18n: ### i18n:
- th_TH (Thai): Added missing translations, reduce transliterated words, fix grammar. - th_TH (Thai): Added missing translations, reduce transliterated words, fix grammar.
## 1.2.40 - 2024-10-23 ## 1.2.40 - 2024-10-23
PR ~~[#40](https://github.com/GreemDev/Ryujinx/pull/40)~~ PR [#40](https://github.com/GreemDev/Ryujinx/pull/40)
Thanks Вова С! Thanks Вова С!
### GUI: ### GUI:
@@ -148,30 +151,30 @@ Thanks Вова С!
- Should prevent crashing on config loads in some circumstances. - Should prevent crashing on config loads in some circumstances.
## 1.2.38 - 2024-10-23 ## 1.2.38 - 2024-10-23
PR [#51](https://web.archive.org/web/20241127022413/https://github.com/GreemDev/Ryujinx/pull/51) PR [#51](https://github.com/GreemDev/Ryujinx/pull/51)
### i18n: ### i18n:
- zh_CH (Simplified Chinese): Add some missing translations. - zh_CH (Simplified Chinese): Add some missing translations.
## 1.2.37 - 2024-10-23 ## 1.2.37 - 2024-10-23
PR [#37](https://web.archive.org/web/20241123010103/https://github.com/GreemDev/Ryujinx/pull/37) PR [#37](https://github.com/GreemDev/Ryujinx/pull/37)
Thanks Last Breath! Thanks Last Breath!
### GUI: ### GUI:
- Set the default controller to the Pro Controller. - Set the default controller to the Pro Controller.
## 1.2.36 - 2024-10-21 ## 1.2.36 - 2024-10-21
PR ~~[#30](https://github.com/GreemDev/Ryujinx/pull/30)~~ PR [#30](https://github.com/GreemDev/Ryujinx/pull/30)
### GUI: ### GUI:
- Fix repeated dialog popup notifying you of new updates when there aren't any, while having a bundled update inside an XCI and an external update file. - Fix repeated dialog popup notifying you of new updates when there aren't any, while having a bundled update inside an XCI and an external update file.
## 1.2.35 - 2024-10-21 ## 1.2.35 - 2024-10-21
PR [#32](https://web.archive.org/web/20241127010942/https://github.com/GreemDev/Ryujinx/pull/32) PR [#32](https://github.com/GreemDev/Ryujinx/pull/32)
### GUI: ### GUI:
- Replace "expand DRAM" option with a DRAM size dropdown. - Replace "expand DRAM" option with a DRAM size dropdown.
- Allows for using mods which require a ridiculous amount of memory to allocate from. - Allows for using mods which require a ridiculous amount of memory to allocate from.
## 1.2.34 - 2024-10-21 ## 1.2.34 - 2024-10-21
PR [#29](https://web.archive.org/web/20241125093029/https://github.com/GreemDev/Ryujinx/pull/29) PR [#29](https://github.com/GreemDev/Ryujinx/pull/29)
### GUI: ### GUI:
- Fix duplicate controller names when 2 controllers of the same type are connected. - Fix duplicate controller names when 2 controllers of the same type are connected.
### INPUT: ### INPUT:
@@ -248,7 +251,7 @@ Added Low-power PPTC mode strings to the translation files.
## 1.2.1-1.2.19 - 2024-10-08 - 2024-10-11 ## 1.2.1-1.2.19 - 2024-10-08 - 2024-10-11
### GUI/INFRA/MISC: ### GUI/INFRA/MISC:
- Remove GTK UI. - Remove GTK UI.
- Autoload DLC/Updates from dir ([#12](https://web.archive.org/web/20241127004005/https://github.com/GreemDev/Ryujinx/pull/12)). - Autoload DLC/Updates from dir ([#12](https://github.com/GreemDev/Ryujinx/pull/12)).
- Changed executable icon to rainbow logo. - Changed executable icon to rainbow logo.
- Extract Data > Logo now also extracts the square thumbnail you see for the game in the UI. - Extract Data > Logo now also extracts the square thumbnail you see for the game in the UI.
- The "use random UUID hack" checkbox in the Amiibo screen now remembers its last state when you reopen the window in a given session. - The "use random UUID hack" checkbox in the Amiibo screen now remembers its last state when you reopen the window in a given session.

View File

@@ -19,8 +19,8 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/> <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
<PackageVersion Include="Concentus" Version="2.2.2" /> <PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="9.4.1" /> <PackageVersion Include="DynamicData" Version="9.0.4" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="Humanizer" Version="2.14.1" /> <PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
@@ -40,13 +40,11 @@
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" /> <PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.116" /> <PackageVersion Include="Ryujinx.LibHac" Version="0.20.0-alpha.107" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" /> <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" /> <PackageVersion Include="Gommon" Version="2.7.1.1" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
<PackageVersion Include="Gommon" Version="2.7.2.1" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.11.1" /> <PackageVersion Include="Sep" Version="0.6.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />

View File

@@ -7,8 +7,8 @@
# Ryujinx # Ryujinx
[![Latest release](https://img.shields.io/gitlab/v/release/ryubing%2Fryujinx?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=stable&color=32cd32)](https://update.ryujinx.app/latest/stable) [![Latest release](https://img.shields.io/github/v/release/Ryubing/Stable-Releases?label=stable)](https://github.com/Ryubing/Stable-Releases/releases/latest)
[![Latest canary release](https://img.shields.io/gitlab/v/release/ryubing%2Fcanary?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=canary&color=FF4500)](https://update.ryujinx.app/latest/canary) [![Latest canary release](https://img.shields.io/github/v/release/Ryubing/Canary-Releases?label=canary)](https://github.com/Ryubing/Canary-Releases/releases/latest)
<br> <br>
<a href="https://discord.gg/PEuzjrFXUA"> <a href="https://discord.gg/PEuzjrFXUA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord"> <img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
@@ -31,7 +31,7 @@
<br> <br>
This is not a Ryujinx revival project. This is not a Phoenix project. This is not a Ryujinx revival project. This is not a Phoenix project.
<br> <br>
Guides and documentation can be found on the <a href="https://git.ryujinx.app/groups/ryubing/-/wikis/home">Wiki tab</a>. Guides and documentation can be found on the <a href="https://git.ryujinx.app/ryubing/ryujinx/-/wikis/home">Wiki tab</a>.
</p> </p>
<p align="center"> <p align="center">
@@ -49,13 +49,13 @@ Stable builds are made every so often, based on the `master` branch, that then g
These stable builds exist so that the end user can get a more **enjoyable and stable experience**. These stable builds exist so that the end user can get a more **enjoyable and stable experience**.
They are released every month or so, to ensure consistent updates, while not being an annoying amount of individual updates to download over the course of that month. They are released every month or so, to ensure consistent updates, while not being an annoying amount of individual updates to download over the course of that month.
You can find the stable releases [here](https://git.ryujinx.app/ryubing/ryujinx/-/releases). You can find the latest stable release [here](https://github.com/Ryubing/Stable-Releases/releases/latest).
Canary builds are compiled automatically for each commit on the `master` branch. Canary builds are compiled automatically for each commit on the `master` branch.
While we strive to ensure optimal stability and performance prior to pushing an update, these builds **may be unstable or completely broken**. While we strive to ensure optimal stability and performance prior to pushing an update, these builds **may be unstable or completely broken**.
These canary builds are only recommended for experienced users. These canary builds are only recommended for experienced users.
You can find the canary releases [here](https://git.ryujinx.app/ryubing/canary/-/releases). You can find the latest canary release [here](https://github.com/Ryubing/Canary-Releases/releases/latest).
## Documentation ## Documentation
@@ -111,7 +111,7 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY
## Credits ## Credits
- [LibHac](https://git.ryujinx.app/ryubing/libhac) is used for our file-system. - [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation. - [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
- [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes. - [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes.
- [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation. - [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation.

View File

@@ -77,8 +77,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
@@ -86,9 +84,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\canary.yml = .github\workflows\canary.yml .github\workflows\canary.yml = .github\workflows\canary.yml
Directory.Packages.props = Directory.Packages.props Directory.Packages.props = Directory.Packages.props
.github\workflows\release.yml = .github\workflows\release.yml .github\workflows\release.yml = .github\workflows\release.yml
nuget.config = nuget.config
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ chmod +x AppDir/AppRun AppDir/usr/bin/Ryujinx*
mkdir -p "$OUTDIR" mkdir -p "$OUTDIR"
appimagetool -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \ appimagetool --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
-u "$UFLAG" \ -u "$UFLAG" \
AppDir "$OUTDIR"/Ryujinx.AppImage AppDir "$OUTDIR"/Ryujinx.AppImage

View File

@@ -33,29 +33,23 @@ echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
echo "Running bundle fix up python script" echo "Running bundle fix up python script"
python3 bundle_fix_up.py "$APP_BUNDLE_DIRECTORY" MacOS/Ryujinx python3 bundle_fix_up.py "$APP_BUNDLE_DIRECTORY" MacOS/Ryujinx
# Resign all dyplib files as ad-hoc after changing them
find "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" -type f -name "*.dylib" -exec codesign --force --sign - {} \;
# Now sign it # Now sign it
echo "Starting signing process" echo "Starting signing process"
if ! [ -x "$(command -v codesign)" ]; if ! [ -x "$(command -v codesign)" ];
then then
if ! [ -x "$(command -v rcodesign)" ]; if ! [ -x "$(command -v rcodesign)" ];
then then
echo "Cannot find rcodesign on your system, please install rcodesign and ensure it is in your search path." echo "Cannot find rcodesign on your system, please install rcodesign."
exit 1 exit 1
fi fi
# cargo install apple-codesign
echo "Using rcodesign for ad-hoc signing" echo "Using rcodesign for ad-hoc signing"
echo "Resigning all frameworks dylib files as ad-hoc"
find "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" -type f -name "*.dylib" -exec rcodesign sign {} \;
echo "Signing app bundle as ad-hoc"
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY" rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY"
else else
echo "Using codesign for ad-hoc signing" echo "Using codesign for ad-hoc signing"
echo "Resigning all frameworks dylib files as ad-hoc"
find "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" -type f -name "*.dylib" -exec codesign --force --sign - {} \;
echo "Signing app bundle as ad-hoc"
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$APP_BUNDLE_DIRECTORY" codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$APP_BUNDLE_DIRECTORY"
fi fi

View File

@@ -20,18 +20,6 @@ SOURCE_REVISION_ID=$6
CONFIGURATION=$7 CONFIGURATION=$7
CANARY=$8 CANARY=$8
if [[ "$(uname)" == "Darwin" ]]; then
echo "Clearing xattr on all dot undercsore files"
find "$BASE_DIR" -type f -name "._*" -exec sh -c '
for f; do
dir=$(dirname "$f")
base=$(basename "$f")
orig="$dir/${base#._}"
[ -f "$orig" ] && xattr -c "$orig" || true
done
' sh {} +
fi
if [ "$CANARY" == "1" ]; then if [ "$CANARY" == "1" ]; then
RELEASE_TAR_FILE_NAME=ryujinx-canary-$VERSION-macos_universal.app.tar RELEASE_TAR_FILE_NAME=ryujinx-canary-$VERSION-macos_universal.app.tar
elif [ "$VERSION" == "1.1.0" ]; then elif [ "$VERSION" == "1.1.0" ]; then

View File

@@ -20,18 +20,6 @@ SOURCE_REVISION_ID=$6
CONFIGURATION=$7 CONFIGURATION=$7
CANARY=$8 CANARY=$8
if [[ "$(uname)" == "Darwin" ]]; then
echo "Clearing xattr on all dot undercsore files"
find "$BASE_DIR" -type f -name "._*" -exec sh -c '
for f; do
dir=$(dirname "$f")
base=$(basename "$f")
orig="$dir/${base#._}"
[ -f "$orig" ] && xattr -c "$orig" || true
done
' sh {} +
fi
if [ "$CANARY" == "1" ]; then if [ "$CANARY" == "1" ]; then
RELEASE_TAR_FILE_NAME=nogui-ryujinx-canary-$VERSION-macos_universal.tar RELEASE_TAR_FILE_NAME=nogui-ryujinx-canary-$VERSION-macos_universal.tar
elif [ "$VERSION" == "1.1.0" ]; then elif [ "$VERSION" == "1.1.0" ]; then

View File

@@ -188,8 +188,6 @@
01003DD00BFEE000,"Airheart - Tales of broken Wings",,playable,2021-02-26 15:20:27 01003DD00BFEE000,"Airheart - Tales of broken Wings",,playable,2021-02-26 15:20:27
01007F100DE52000,"Akane",nvdec,playable,2022-07-21 00:12:18 01007F100DE52000,"Akane",nvdec,playable,2022-07-21 00:12:18
01009A800F0C8000,"Akash: Path of the Five",gpu;nvdec,ingame,2020-12-14 22:33:12 01009A800F0C8000,"Akash: Path of the Five",gpu;nvdec,ingame,2020-12-14 22:33:12
01009E8012976000,"AKIBA'S TRIP: Hellbound & Debriefed",,playable,2025-07-30 23:22:47
0100D74019A0E000,"AKIBA'S TRIP: Undead & Undressed Director's Cut",,playable,2025-07-31 13:58:42
010053100B0EA000,"Akihabara - Feel the Rhythm Remixed",,playable,2021-02-22 14:39:35 010053100B0EA000,"Akihabara - Feel the Rhythm Remixed",,playable,2021-02-22 14:39:35
0100D4C00EE0C000,"Akuarium",slow,playable,2020-12-12 23:43:36 0100D4C00EE0C000,"Akuarium",slow,playable,2020-12-12 23:43:36
010026E00FEBE000,"Akuto: Showdown",,playable,2020-08-04 19:43:27 010026E00FEBE000,"Akuto: Showdown",,playable,2020-08-04 19:43:27
@@ -978,7 +976,7 @@
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07 0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19 010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
01008CB01E52E000,"DOOM + DOOM II",opengl;ldn-untested;LAN,playable,2024-09-12 07:06:01 01008CB01E52E000,"DOOM + DOOM II",opengl;ldn-untested;LAN,playable,2024-09-12 07:06:01
010029D00E740000,"DOOM 3",crash;slow,menus,2024-08-03 05:25:47 010029D00E740000,"DOOM 3",crash,menus,2024-08-03 05:25:47
01005D700E742000,"DOOM 64",nvdec;vulkan,playable,2020-10-13 23:47:28 01005D700E742000,"DOOM 64",nvdec;vulkan,playable,2020-10-13 23:47:28
0100D4F00DD02000,"DOOM II (Classic)",nvdec;online,playable,2021-06-03 20:10:01 0100D4F00DD02000,"DOOM II (Classic)",nvdec;online,playable,2021-06-03 20:10:01
0100B1A00D8CE000,"DOOM® Eternal",gpu;slow;nvdec;online-broken,ingame,2024-08-28 15:57:17 0100B1A00D8CE000,"DOOM® Eternal",gpu;slow;nvdec;online-broken,ingame,2024-08-28 15:57:17
@@ -1097,7 +1095,6 @@
0100F9600E746000,"ESP Ra.De. Psi",audio;slow,ingame,2024-03-07 15:05:08 0100F9600E746000,"ESP Ra.De. Psi",audio;slow,ingame,2024-03-07 15:05:08
010073000FE18000,"Esports powerful pro yakyuu 2020",gpu;crash;Needs More Attention,ingame,2024-04-29 05:34:14 010073000FE18000,"Esports powerful pro yakyuu 2020",gpu;crash;Needs More Attention,ingame,2024-04-29 05:34:14
01004F9012FD8000,"Estranged: The Departure",nvdec;UE4,playable,2022-10-24 10:37:58 01004F9012FD8000,"Estranged: The Departure",nvdec;UE4,playable,2022-10-24 10:37:58
010018F01E0A0000,"Eternights",,playable,2025-07-30 12:10:24
0100CB900B498000,"Eternum Ex",,playable,2021-01-13 20:28:32 0100CB900B498000,"Eternum Ex",,playable,2021-01-13 20:28:32
010092501EB2C000,"Europa (Demo)",gpu;crash;UE4,ingame,2024-04-23 10:47:12 010092501EB2C000,"Europa (Demo)",gpu;crash;UE4,ingame,2024-04-23 10:47:12
01007BE0160D6000,"EVE ghost enemies",gpu,ingame,2023-01-14 03:13:30 01007BE0160D6000,"EVE ghost enemies",gpu,ingame,2023-01-14 03:13:30
@@ -1128,7 +1125,6 @@
0100034012606000,"Family Mysteries: Poisonous Promises",audio;crash,menus,2021-11-26 12:35:06 0100034012606000,"Family Mysteries: Poisonous Promises",audio;crash,menus,2021-11-26 12:35:06
010017C012726000,"Fantasy Friends",,playable,2022-10-17 19:42:39 010017C012726000,"Fantasy Friends",,playable,2022-10-17 19:42:39
0100767008502000,"FANTASY HERO unsigned legacy",,playable,2022-07-26 12:28:52 0100767008502000,"FANTASY HERO unsigned legacy",,playable,2022-07-26 12:28:52
0100755017EE0000,"FANTASY LIFE i: The Girl Who Steals Time",gpu;crash;vulkan-backend-bug,ingame,2025-06-08 20:41:00
0100944003820000,"Fantasy Strike",online,playable,2021-02-27 01:59:18 0100944003820000,"Fantasy Strike",online,playable,2021-02-27 01:59:18
01000E2012F6E000,"Fantasy Tavern Sextet -Vol.1 New World Days-",gpu;crash;Needs Update,ingame,2022-12-05 16:48:00 01000E2012F6E000,"Fantasy Tavern Sextet -Vol.1 New World Days-",gpu;crash;Needs Update,ingame,2022-12-05 16:48:00
01005C10136CA000,"Fantasy Tavern Sextet -Vol.2 Adventurer's Days-",gpu;slow;crash,ingame,2021-11-06 02:57:29 01005C10136CA000,"Fantasy Tavern Sextet -Vol.2 Adventurer's Days-",gpu;slow;crash,ingame,2021-11-06 02:57:29
@@ -1243,8 +1239,6 @@
010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38 010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38
010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27 010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27
0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24 0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24
0100C4E018A24000,"FRONT MISSION 2: Remake",,playable,2025-07-30 12:11:23
01007E6019872000,"FRONT MISSION 3: Remake",,playable,2025-07-30 12:12:02
0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59 0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59
0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36 0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36
010038A007AA4000,"FruitFall Crush",,playable,2020-10-20 11:33:33 010038A007AA4000,"FruitFall Crush",,playable,2020-10-20 11:33:33
@@ -1440,7 +1434,6 @@
0100C2700E338000,"Heroland",,playable,2020-08-05 15:35:39 0100C2700E338000,"Heroland",,playable,2020-08-05 15:35:39
01007AC00E012000,"HexaGravity",,playable,2021-05-28 13:47:48 01007AC00E012000,"HexaGravity",,playable,2021-05-28 13:47:48
01004E800F03C000,"Hidden",slow,ingame,2022-10-05 10:56:53 01004E800F03C000,"Hidden",slow,ingame,2022-10-05 10:56:53
0100C1101EE5A000,"High on Life",,menus,2025-08-26 19:11:00
0100F6A00A684000,"Higurashi no Naku Koro ni Hō",audio,ingame,2021-09-18 14:40:28 0100F6A00A684000,"Higurashi no Naku Koro ni Hō",audio,ingame,2021-09-18 14:40:28
0100F8D0129F4000,"Himehibi 1 gakki - Princess Days",crash,nothing,2021-11-03 08:34:19 0100F8D0129F4000,"Himehibi 1 gakki - Princess Days",crash,nothing,2021-11-03 08:34:19
0100F3D008436000,"Hiragana Pixel Party",,playable,2021-01-14 08:36:50 0100F3D008436000,"Hiragana Pixel Party",,playable,2021-01-14 08:36:50
@@ -1450,7 +1443,6 @@
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35 0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58 0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56 0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
010013C00E930000,"Hollow Knight: Silksong",,playable,2025-09-04 17:23:22
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56 0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56 0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38 010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38
@@ -1525,7 +1517,6 @@
010095C016C14000,"Iridium",,playable,2022-08-05 23:19:53 010095C016C14000,"Iridium",,playable,2022-08-05 23:19:53
0100AD300B786000,"Iris School of Wizardry -Vinculum Hearts-",,playable,2022-12-05 13:11:15 0100AD300B786000,"Iris School of Wizardry -Vinculum Hearts-",,playable,2022-12-05 13:11:15
0100945012168000,"Iris.Fall",nvdec,playable,2022-10-18 13:40:22 0100945012168000,"Iris.Fall",nvdec,playable,2022-10-18 13:40:22
010059801B736000,"IronFall: Invasion",,playable,2025-07-30 11:42:30
01005270118D6000,"Iron Wings",slow,ingame,2022-08-07 08:32:57 01005270118D6000,"Iron Wings",slow,ingame,2022-08-07 08:32:57
01004DB003E6A000,"IRONCAST",,playable,2021-01-13 13:54:29 01004DB003E6A000,"IRONCAST",,playable,2021-01-13 13:54:29
0100E5700CD56000,"Irony Curtain: From Matryoshka with Love",,playable,2021-06-04 20:12:37 0100E5700CD56000,"Irony Curtain: From Matryoshka with Love",,playable,2021-06-04 20:12:37
@@ -1889,7 +1880,7 @@
010097800EA20000,"Monster Energy Supercross - The Official Videogame 3",UE4;audout;nvdec;online,playable,2021-06-14 12:37:54 010097800EA20000,"Monster Energy Supercross - The Official Videogame 3",UE4;audout;nvdec;online,playable,2021-06-14 12:37:54
0100E9900ED74000,"Monster Farm",32-bit;nvdec,playable,2021-05-05 19:29:13 0100E9900ED74000,"Monster Farm",32-bit;nvdec,playable,2021-05-05 19:29:13
0100770008DD8000,"Monster Hunter Generations Ultimate™",32-bit;online-broken;ldn-works,playable,2024-03-18 14:35:36 0100770008DD8000,"Monster Hunter Generations Ultimate™",32-bit;online-broken;ldn-works,playable,2024-03-18 14:35:36
0100B04011742000,"MONSTER HUNTER RISE",gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works,ingame,2024-08-24 11:04:59 0100B04011742000,"Monster Hunter Rise",gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works,ingame,2024-08-24 11:04:59
010093A01305C000,"Monster Hunter Rise Demo",online-broken;ldn-works;demo,playable,2022-10-18 23:04:17 010093A01305C000,"Monster Hunter Rise Demo",online-broken;ldn-works;demo,playable,2022-10-18 23:04:17
0100E21011446000,"Monster Hunter Stories 2: Wings of Ruin",services,ingame,2022-07-10 19:27:30 0100E21011446000,"Monster Hunter Stories 2: Wings of Ruin",services,ingame,2022-07-10 19:27:30
010042501329E000,"MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version",demo,playable,2022-11-13 22:20:26 010042501329E000,"MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version",demo,playable,2022-11-13 22:20:26
@@ -2265,7 +2256,6 @@
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00 010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54 0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04 010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04
010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00
01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32 01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32
01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22 01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22
0100ABF008968000,"Pokémon Sword + Pokémon Sword Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-26 15:40:37 0100ABF008968000,"Pokémon Sword + Pokémon Sword Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-26 15:40:37
@@ -2314,7 +2304,6 @@
010077B00BDD8000,"Professional Farmer: Nintendo Switch™ Edition",slow,playable,2020-12-16 13:38:19 010077B00BDD8000,"Professional Farmer: Nintendo Switch™ Edition",slow,playable,2020-12-16 13:38:19
010018300C83A000,"Professor Lupo and his Horrible Pets",,playable,2020-06-12 00:08:45 010018300C83A000,"Professor Lupo and his Horrible Pets",,playable,2020-06-12 00:08:45
0100D1F0132F6000,"Professor Lupo: Ocean",,playable,2021-04-14 16:33:33 0100D1F0132F6000,"Professor Lupo: Ocean",,playable,2021-04-14 16:33:33
0100C3A017834000,"Prodeus",,playable,2025-07-30 12:07:52
0100BBD00976C000,"Project Highrise: Architect's Edition",,playable,2022-08-10 17:19:12 0100BBD00976C000,"Project Highrise: Architect's Edition",,playable,2022-08-10 17:19:12
0100ACE00DAB6000,"Project Nimbus: Complete Edition",nvdec;UE4;vulkan-backend-bug,playable,2022-08-10 17:35:43 0100ACE00DAB6000,"Project Nimbus: Complete Edition",nvdec;UE4;vulkan-backend-bug,playable,2022-08-10 17:35:43
01002980140F6000,"Project TRIANGLE STRATEGY™ Debut Demo",UE4;demo,playable,2022-10-24 21:40:27 01002980140F6000,"Project TRIANGLE STRATEGY™ Debut Demo",UE4;demo,playable,2022-10-24 21:40:27
@@ -2446,7 +2435,6 @@
0100E9C010EA8000,"Rise of Insanity",,playable,2020-08-30 15:42:14 0100E9C010EA8000,"Rise of Insanity",,playable,2020-08-30 15:42:14
01006BA00E652000,"Rise: Race The Future",,playable,2021-02-27 13:29:06 01006BA00E652000,"Rise: Race The Future",,playable,2021-02-27 13:29:06
010020C012F48000,"Rising Hell",,playable,2022-10-31 13:54:02 010020C012F48000,"Rising Hell",,playable,2022-10-31 13:54:02
0100D1801A0F4000,"Risk of Rain Returns",,playable,2025-06-28 04:24:04
010076D00E4BA000,"Risk of Rain 2",online-broken,playable,2024-03-04 17:01:05 010076D00E4BA000,"Risk of Rain 2",online-broken,playable,2024-03-04 17:01:05
0100E8300A67A000,"RISK® Global Domination",nvdec;online-broken,playable,2022-08-01 18:53:28 0100E8300A67A000,"RISK® Global Domination",nvdec;online-broken,playable,2022-08-01 18:53:28
010042500FABA000,"Ritual: Crown of Horns",,playable,2021-01-26 16:01:47 010042500FABA000,"Ritual: Crown of Horns",,playable,2021-01-26 16:01:47
@@ -2580,7 +2568,6 @@
0100C610154CA000,"Shadowrun: Hong Kong - Extended Edition",gpu;Needs Update,ingame,2022-10-04 20:53:09 0100C610154CA000,"Shadowrun: Hong Kong - Extended Edition",gpu;Needs Update,ingame,2022-10-04 20:53:09
010000000EEF0000,"Shadows 2: Perfidia",,playable,2020-08-07 12:43:46 010000000EEF0000,"Shadows 2: Perfidia",,playable,2020-08-07 12:43:46
0100AD700CBBE000,"Shadows of Adam",,playable,2021-01-11 13:35:58 0100AD700CBBE000,"Shadows of Adam",,playable,2021-01-11 13:35:58
010037A01F96C000,"Shadows of the Damned: Hella Remastered",,playable,2025-09-05 11:34:32
01002A800C064000,"Shadowverse Champions Battle",,playable,2022-10-02 22:59:29 01002A800C064000,"Shadowverse Champions Battle",,playable,2022-10-02 22:59:29
01003B90136DA000,"Shadowverse: Champion's Battle",crash,nothing,2023-03-06 00:31:50 01003B90136DA000,"Shadowverse: Champion's Battle",crash,nothing,2023-03-06 00:31:50
0100820013612000,"Shady Part of Me",,playable,2022-10-20 11:31:55 0100820013612000,"Shady Part of Me",,playable,2022-10-20 11:31:55
@@ -2758,7 +2745,6 @@
01005D701264A000,"SpyHack",,playable,2021-04-15 10:53:51 01005D701264A000,"SpyHack",,playable,2021-04-15 10:53:51
010077B00E046000,"Spyro™ Reignited Trilogy",nvdec;UE4,playable,2022-09-11 18:38:33 010077B00E046000,"Spyro™ Reignited Trilogy",nvdec;UE4,playable,2022-09-11 18:38:33
0100085012A0E000,"Squeakers",,playable,2020-12-13 12:13:05 0100085012A0E000,"Squeakers",,playable,2020-12-13 12:13:05
0100E1D01EB2E000,"Squeakross: Home Squeak Home",,playable,2025-06-16 02:02:00
010009300D31C000,"Squidgies Takeover",,playable,2020-07-20 22:28:08 010009300D31C000,"Squidgies Takeover",,playable,2020-07-20 22:28:08
0100FCD0102EC000,"Squidlit",,playable,2020-08-06 12:38:32 0100FCD0102EC000,"Squidlit",,playable,2020-08-06 12:38:32
0100EBF00E702000,"STAR OCEAN First Departure R",nvdec,playable,2021-07-05 19:29:16 0100EBF00E702000,"STAR OCEAN First Departure R",nvdec,playable,2021-07-05 19:29:16
@@ -2778,7 +2764,7 @@
0100E6B0115FC000,"Star99",online,menus,2021-11-26 14:18:51 0100E6B0115FC000,"Star99",online,menus,2021-11-26 14:18:51
01002100137BA000,"Stardash",,playable,2021-01-21 16:31:19 01002100137BA000,"Stardash",,playable,2021-01-21 16:31:19
0100E65002BB8000,"Stardew Valley",online-broken;ldn-untested,playable,2024-02-14 03:11:19 0100E65002BB8000,"Stardew Valley",online-broken;ldn-untested,playable,2024-02-14 03:11:19
01002CC003FE6000,"Starlink: Battle for Atlas™ Digital Edition",,playable,2025-07-30 12:09:37 01002CC003FE6000,"Starlink: Battle for Atlas™ Digital Edition",services-horizon;crash;Needs Update,nothing,2024-05-05 17:25:11
010098E010FDA000,"Starlit Adventures Golden Stars",,playable,2020-11-21 12:14:43 010098E010FDA000,"Starlit Adventures Golden Stars",,playable,2020-11-21 12:14:43
01001BB00AC26000,"STARSHIP AVENGER Operation: Take Back Earth",,playable,2021-01-12 15:52:55 01001BB00AC26000,"STARSHIP AVENGER Operation: Take Back Earth",,playable,2021-01-12 15:52:55
010000700A572000,"State of Anarchy: Master of Mayhem",nvdec,playable,2021-01-12 19:00:05 010000700A572000,"State of Anarchy: Master of Mayhem",nvdec,playable,2021-01-12 19:00:05
@@ -2979,7 +2965,6 @@
0100EBA01548E000,"The Cruel King and the Great Hero",gpu;services,ingame,2022-12-02 07:02:08 0100EBA01548E000,"The Cruel King and the Great Hero",gpu;services,ingame,2022-12-02 07:02:08
010051800E922000,"The Dark Crystal: Age of Resistance Tactics",,playable,2020-08-11 13:43:41 010051800E922000,"The Dark Crystal: Age of Resistance Tactics",,playable,2020-08-11 13:43:41
01003DE00918E000,"The Darkside Detective",,playable,2020-06-03 22:16:18 01003DE00918E000,"The Darkside Detective",,playable,2020-06-03 22:16:18
010032B015D66000,"The DioField Chronicle",,playable,2025-09-05 11:35:50
01000A10041EA000,"The Elder Scrolls V: Skyrim",gpu;crash,ingame,2024-07-14 03:21:31 01000A10041EA000,"The Elder Scrolls V: Skyrim",gpu;crash,ingame,2024-07-14 03:21:31
01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45 01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45
0100CA100489C000,"The Escapists 2",nvdec,playable,2020-09-24 12:31:31 0100CA100489C000,"The Escapists 2",nvdec,playable,2020-09-24 12:31:31
@@ -2987,7 +2972,6 @@
0100C2E0129A6000,"The Executioner",nvdec,playable,2021-01-23 00:31:28 0100C2E0129A6000,"The Executioner",nvdec,playable,2021-01-23 00:31:28
01006050114D4000,"The Experiment: Escape Room",gpu,ingame,2022-09-30 13:20:35 01006050114D4000,"The Experiment: Escape Room",gpu,ingame,2022-09-30 13:20:35
0100B5900DFB2000,"The Eyes of Ara",,playable,2022-09-16 14:44:06 0100B5900DFB2000,"The Eyes of Ara",,playable,2022-09-16 14:44:06
0100BA5013E52000,"The Falconeer: Warrior Edition",,playable,2025-07-30 12:04:50
01002DD00AF9E000,"The Fall",gpu,ingame,2020-05-31 23:31:16 01002DD00AF9E000,"The Fall",gpu,ingame,2020-05-31 23:31:16
01003E5002320000,"The Fall Part 2: Unbound",,playable,2021-11-06 02:18:08 01003E5002320000,"The Fall Part 2: Unbound",,playable,2021-11-06 02:18:08
0100CDC00789E000,"The Final Station",nvdec,playable,2022-08-22 15:54:39 0100CDC00789E000,"The Final Station",nvdec,playable,2022-08-22 15:54:39
@@ -3031,7 +3015,6 @@
01009B101044C000,"The Legend of Heroes: Trails of Cold Steel III Demo",demo;nvdec,playable,2021-04-23 01:07:32 01009B101044C000,"The Legend of Heroes: Trails of Cold Steel III Demo",demo;nvdec,playable,2021-04-23 01:07:32
0100D3C010DE8000,"The Legend of Heroes: Trails of Cold Steel IV",nvdec,playable,2021-04-23 14:01:05 0100D3C010DE8000,"The Legend of Heroes: Trails of Cold Steel IV",nvdec,playable,2021-04-23 14:01:05
01005E5013862000,"THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改]",crash,nothing,2021-09-30 14:41:07 01005E5013862000,"THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改]",crash,nothing,2021-09-30 14:41:07
01009C901ACEE000,"The Legend of Nayuta: Boundless Trails",,ingame,2025-06-12 15:47
01008CF01BAAC000,"The Legend of Zelda Echoes of Wisdom",nvdec;ASTC;intel-vendor-bug,playable,2024-10-01 14:11:01 01008CF01BAAC000,"The Legend of Zelda Echoes of Wisdom",nvdec;ASTC;intel-vendor-bug,playable,2024-10-01 14:11:01
0100509005AF2000,"The Legend of Zelda: Breath of the Wild Demo",demo,ingame,2022-12-24 05:02:58 0100509005AF2000,"The Legend of Zelda: Breath of the Wild Demo",demo,ingame,2022-12-24 05:02:58
01007EF00011E000,"The Legend of Zelda™: Breath of the Wild",gpu;amd-vendor-bug;mac-bug,ingame,2024-09-23 19:35:46 01007EF00011E000,"The Legend of Zelda™: Breath of the Wild",gpu;amd-vendor-bug;mac-bug,ingame,2024-09-23 19:35:46
@@ -3210,7 +3193,6 @@
010000400F582000,"TT Isle of Man Ride on the Edge 2",gpu;nvdec;online-broken,ingame,2022-09-30 22:13:05 010000400F582000,"TT Isle of Man Ride on the Edge 2",gpu;nvdec;online-broken,ingame,2022-09-30 22:13:05
0100752011628000,"TTV2",,playable,2020-11-27 13:21:36 0100752011628000,"TTV2",,playable,2020-11-27 13:21:36
0100AFE00452E000,"Tumblestone",,playable,2021-01-07 17:49:20 0100AFE00452E000,"Tumblestone",,playable,2021-01-07 17:49:20
0100D1A01D7BA000,"Turbo Overkill",,playable,2025-07-30 12:08:57
010085500D5F6000,"Turok",gpu,ingame,2021-06-04 13:16:24 010085500D5F6000,"Turok",gpu,ingame,2021-06-04 13:16:24
0100CDC00D8D6000,"Turok 2: Seeds of Evil",gpu;vulkan,ingame,2022-09-12 17:50:05 0100CDC00D8D6000,"Turok 2: Seeds of Evil",gpu;vulkan,ingame,2022-09-12 17:50:05
010004B0130C8000,"Turrican Flashback",audout,playable,2021-08-30 10:07:56 010004B0130C8000,"Turrican Flashback",audout,playable,2021-08-30 10:07:56
@@ -3234,8 +3216,6 @@
0100592005164000,"UNBOX: Newbie's Adventure",UE4,playable,2022-08-29 13:12:56 0100592005164000,"UNBOX: Newbie's Adventure",UE4,playable,2022-08-29 13:12:56
01002D900C5E4000,"Uncanny Valley",nvdec,playable,2021-06-04 13:28:45 01002D900C5E4000,"Uncanny Valley",nvdec,playable,2021-06-04 13:28:45
010076F011F54000,"Undead & Beyond",nvdec,playable,2022-10-04 09:11:18 010076F011F54000,"Undead & Beyond",nvdec,playable,2022-10-04 09:11:18
01009B700D0B8000,"Undead Horde",,playable,2025-07-30 12:05:05
0100FC301A878000,"Undead Horde 2: Necropolis",,playable,2025-07-30 12:06:07
01008F3013E4E000,"Under Leaves",,playable,2021-05-22 18:13:58 01008F3013E4E000,"Under Leaves",,playable,2021-05-22 18:13:58
010080B00AD66000,"Undertale",,playable,2022-08-31 17:31:46 010080B00AD66000,"Undertale",,playable,2022-08-31 17:31:46
01008F80049C6000,"Unepic",,playable,2024-01-15 17:03:00 01008F80049C6000,"Unepic",,playable,2024-01-15 17:03:00
1 title_id game_name labels status last_updated
188 01003DD00BFEE000 Airheart - Tales of broken Wings playable 2021-02-26 15:20:27
189 01007F100DE52000 Akane nvdec playable 2022-07-21 00:12:18
190 01009A800F0C8000 Akash: Path of the Five gpu;nvdec ingame 2020-12-14 22:33:12
01009E8012976000 AKIBA'S TRIP: Hellbound & Debriefed playable 2025-07-30 23:22:47
0100D74019A0E000 AKIBA'S TRIP: Undead & Undressed Director's Cut playable 2025-07-31 13:58:42
191 010053100B0EA000 Akihabara - Feel the Rhythm Remixed playable 2021-02-22 14:39:35
192 0100D4C00EE0C000 Akuarium slow playable 2020-12-12 23:43:36
193 010026E00FEBE000 Akuto: Showdown playable 2020-08-04 19:43:27
976 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
977 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19
978 01008CB01E52E000 DOOM + DOOM II opengl;ldn-untested;LAN playable 2024-09-12 07:06:01
979 010029D00E740000 DOOM 3 crash;slow crash menus 2024-08-03 05:25:47
980 01005D700E742000 DOOM 64 nvdec;vulkan playable 2020-10-13 23:47:28
981 0100D4F00DD02000 DOOM II (Classic) nvdec;online playable 2021-06-03 20:10:01
982 0100B1A00D8CE000 DOOM® Eternal gpu;slow;nvdec;online-broken ingame 2024-08-28 15:57:17
1095 0100F9600E746000 ESP Ra.De. Psi audio;slow ingame 2024-03-07 15:05:08
1096 010073000FE18000 Esports powerful pro yakyuu 2020 gpu;crash;Needs More Attention ingame 2024-04-29 05:34:14
1097 01004F9012FD8000 Estranged: The Departure nvdec;UE4 playable 2022-10-24 10:37:58
010018F01E0A0000 Eternights playable 2025-07-30 12:10:24
1098 0100CB900B498000 Eternum Ex playable 2021-01-13 20:28:32
1099 010092501EB2C000 Europa (Demo) gpu;crash;UE4 ingame 2024-04-23 10:47:12
1100 01007BE0160D6000 EVE ghost enemies gpu ingame 2023-01-14 03:13:30
1125 0100034012606000 Family Mysteries: Poisonous Promises audio;crash menus 2021-11-26 12:35:06
1126 010017C012726000 Fantasy Friends playable 2022-10-17 19:42:39
1127 0100767008502000 FANTASY HERO ~unsigned legacy~ playable 2022-07-26 12:28:52
0100755017EE0000 FANTASY LIFE i: The Girl Who Steals Time gpu;crash;vulkan-backend-bug ingame 2025-06-08 20:41:00
1128 0100944003820000 Fantasy Strike online playable 2021-02-27 01:59:18
1129 01000E2012F6E000 Fantasy Tavern Sextet -Vol.1 New World Days- gpu;crash;Needs Update ingame 2022-12-05 16:48:00
1130 01005C10136CA000 Fantasy Tavern Sextet -Vol.2 Adventurer's Days- gpu;slow;crash ingame 2021-11-06 02:57:29
1239 010003F00BD48000 Friday the 13th: Killer Puzzle playable 2021-01-28 01:33:38
1240 010092A00C4B6000 Friday the 13th: The Game Ultimate Slasher Edition nvdec;online-broken;UE4 playable 2022-09-06 17:33:27
1241 0100F200178F4000 FRONT MISSION 1st: Remake playable 2023-06-09 07:44:24
0100C4E018A24000 FRONT MISSION 2: Remake playable 2025-07-30 12:11:23
01007E6019872000 FRONT MISSION 3: Remake playable 2025-07-30 12:12:02
1242 0100861012474000 Frontline Zed playable 2020-10-03 12:55:59
1243 0100B5300B49A000 Frost playable 2022-07-27 12:00:36
1244 010038A007AA4000 FruitFall Crush playable 2020-10-20 11:33:33
1434 0100C2700E338000 Heroland playable 2020-08-05 15:35:39
1435 01007AC00E012000 HexaGravity playable 2021-05-28 13:47:48
1436 01004E800F03C000 Hidden slow ingame 2022-10-05 10:56:53
0100C1101EE5A000 High on Life menus 2025-08-26 19:11:00
1437 0100F6A00A684000 Higurashi no Naku Koro ni Hō audio ingame 2021-09-18 14:40:28
1438 0100F8D0129F4000 Himehibi 1 gakki - Princess Days crash nothing 2021-11-03 08:34:19
1439 0100F3D008436000 Hiragana Pixel Party playable 2021-01-14 08:36:50
1443 0100F7300ED2C000 Hoggy2 playable 2022-10-10 13:53:35
1444 0100F7E00C70E000 Hogwarts Legacy UE4;slow ingame 2024-09-03 19:53:58
1445 0100633007D48000 Hollow Knight nvdec playable 2023-01-16 15:44:56
010013C00E930000 Hollow Knight: Silksong playable 2025-09-04 17:23:22
1446 0100F2100061E800 Hollow0 UE4;gpu ingame 2021-03-03 23:42:56
1447 0100342009E16000 Holy Potatoes! What The Hell?! playable 2020-07-03 10:48:56
1448 010071B00C904000 HoPiKo playable 2021-01-13 20:12:38
1517 010095C016C14000 Iridium playable 2022-08-05 23:19:53
1518 0100AD300B786000 Iris School of Wizardry -Vinculum Hearts- playable 2022-12-05 13:11:15
1519 0100945012168000 Iris.Fall nvdec playable 2022-10-18 13:40:22
010059801B736000 IronFall: Invasion playable 2025-07-30 11:42:30
1520 01005270118D6000 Iron Wings slow ingame 2022-08-07 08:32:57
1521 01004DB003E6A000 IRONCAST playable 2021-01-13 13:54:29
1522 0100E5700CD56000 Irony Curtain: From Matryoshka with Love playable 2021-06-04 20:12:37
1880 010097800EA20000 Monster Energy Supercross - The Official Videogame 3 UE4;audout;nvdec;online playable 2021-06-14 12:37:54
1881 0100E9900ED74000 Monster Farm 32-bit;nvdec playable 2021-05-05 19:29:13
1882 0100770008DD8000 Monster Hunter Generations Ultimate™ 32-bit;online-broken;ldn-works playable 2024-03-18 14:35:36
1883 0100B04011742000 MONSTER HUNTER RISE Monster Hunter Rise gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works ingame 2024-08-24 11:04:59
1884 010093A01305C000 Monster Hunter Rise Demo online-broken;ldn-works;demo playable 2022-10-18 23:04:17
1885 0100E21011446000 Monster Hunter Stories 2: Wings of Ruin services ingame 2022-07-10 19:27:30
1886 010042501329E000 MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version demo playable 2022-11-13 22:20:26
2256 010086F0064CE000 Poi: Explorer Edition nvdec playable 2021-01-21 19:32:00
2257 0100EB6012FD2000 Poison Control playable 2021-05-16 14:01:54
2258 010072400E04A000 Pokémon Café ReMix playable 2021-08-17 20:00:04
010008c01e742000 Pokémon Friends crash;services menus 2025-07-24 13:32:00
2259 01003D200BAA2000 Pokémon Mystery Dungeon™: Rescue Team DX mac-bug playable 2024-01-21 00:16:32
2260 01008DB008C2C000 Pokémon Shield + Pokémon Shield Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-12 07:20:22
2261 0100ABF008968000 Pokémon Sword + Pokémon Sword Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-26 15:40:37
2304 010077B00BDD8000 Professional Farmer: Nintendo Switch™ Edition slow playable 2020-12-16 13:38:19
2305 010018300C83A000 Professor Lupo and his Horrible Pets playable 2020-06-12 00:08:45
2306 0100D1F0132F6000 Professor Lupo: Ocean playable 2021-04-14 16:33:33
0100C3A017834000 Prodeus playable 2025-07-30 12:07:52
2307 0100BBD00976C000 Project Highrise: Architect's Edition playable 2022-08-10 17:19:12
2308 0100ACE00DAB6000 Project Nimbus: Complete Edition nvdec;UE4;vulkan-backend-bug playable 2022-08-10 17:35:43
2309 01002980140F6000 Project TRIANGLE STRATEGY™ Debut Demo UE4;demo playable 2022-10-24 21:40:27
2435 0100E9C010EA8000 Rise of Insanity playable 2020-08-30 15:42:14
2436 01006BA00E652000 Rise: Race The Future playable 2021-02-27 13:29:06
2437 010020C012F48000 Rising Hell playable 2022-10-31 13:54:02
0100D1801A0F4000 Risk of Rain Returns playable 2025-06-28 04:24:04
2438 010076D00E4BA000 Risk of Rain 2 online-broken playable 2024-03-04 17:01:05
2439 0100E8300A67A000 RISK® Global Domination nvdec;online-broken playable 2022-08-01 18:53:28
2440 010042500FABA000 Ritual: Crown of Horns playable 2021-01-26 16:01:47
2568 0100C610154CA000 Shadowrun: Hong Kong - Extended Edition gpu;Needs Update ingame 2022-10-04 20:53:09
2569 010000000EEF0000 Shadows 2: Perfidia playable 2020-08-07 12:43:46
2570 0100AD700CBBE000 Shadows of Adam playable 2021-01-11 13:35:58
010037A01F96C000 Shadows of the Damned: Hella Remastered playable 2025-09-05 11:34:32
2571 01002A800C064000 Shadowverse Champions Battle playable 2022-10-02 22:59:29
2572 01003B90136DA000 Shadowverse: Champion's Battle crash nothing 2023-03-06 00:31:50
2573 0100820013612000 Shady Part of Me playable 2022-10-20 11:31:55
2745 01005D701264A000 SpyHack playable 2021-04-15 10:53:51
2746 010077B00E046000 Spyro™ Reignited Trilogy nvdec;UE4 playable 2022-09-11 18:38:33
2747 0100085012A0E000 Squeakers playable 2020-12-13 12:13:05
0100E1D01EB2E000 Squeakross: Home Squeak Home playable 2025-06-16 02:02:00
2748 010009300D31C000 Squidgies Takeover playable 2020-07-20 22:28:08
2749 0100FCD0102EC000 Squidlit playable 2020-08-06 12:38:32
2750 0100EBF00E702000 STAR OCEAN First Departure R nvdec playable 2021-07-05 19:29:16
2764 0100E6B0115FC000 Star99 online menus 2021-11-26 14:18:51
2765 01002100137BA000 Stardash playable 2021-01-21 16:31:19
2766 0100E65002BB8000 Stardew Valley online-broken;ldn-untested playable 2024-02-14 03:11:19
2767 01002CC003FE6000 Starlink: Battle for Atlas™ Digital Edition services-horizon;crash;Needs Update playable nothing 2025-07-30 12:09:37 2024-05-05 17:25:11
2768 010098E010FDA000 Starlit Adventures Golden Stars playable 2020-11-21 12:14:43
2769 01001BB00AC26000 STARSHIP AVENGER Operation: Take Back Earth playable 2021-01-12 15:52:55
2770 010000700A572000 State of Anarchy: Master of Mayhem nvdec playable 2021-01-12 19:00:05
2965 0100EBA01548E000 The Cruel King and the Great Hero gpu;services ingame 2022-12-02 07:02:08
2966 010051800E922000 The Dark Crystal: Age of Resistance Tactics playable 2020-08-11 13:43:41
2967 01003DE00918E000 The Darkside Detective playable 2020-06-03 22:16:18
010032B015D66000 The DioField Chronicle playable 2025-09-05 11:35:50
2968 01000A10041EA000 The Elder Scrolls V: Skyrim gpu;crash ingame 2024-07-14 03:21:31
2969 01004A9006B84000 The End Is Nigh playable 2020-06-01 11:26:45
2970 0100CA100489C000 The Escapists 2 nvdec playable 2020-09-24 12:31:31
2972 0100C2E0129A6000 The Executioner nvdec playable 2021-01-23 00:31:28
2973 01006050114D4000 The Experiment: Escape Room gpu ingame 2022-09-30 13:20:35
2974 0100B5900DFB2000 The Eyes of Ara playable 2022-09-16 14:44:06
0100BA5013E52000 The Falconeer: Warrior Edition playable 2025-07-30 12:04:50
2975 01002DD00AF9E000 The Fall gpu ingame 2020-05-31 23:31:16
2976 01003E5002320000 The Fall Part 2: Unbound playable 2021-11-06 02:18:08
2977 0100CDC00789E000 The Final Station nvdec playable 2022-08-22 15:54:39
3015 01009B101044C000 The Legend of Heroes: Trails of Cold Steel III Demo demo;nvdec playable 2021-04-23 01:07:32
3016 0100D3C010DE8000 The Legend of Heroes: Trails of Cold Steel IV nvdec playable 2021-04-23 14:01:05
3017 01005E5013862000 THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改] crash nothing 2021-09-30 14:41:07
01009C901ACEE000 The Legend of Nayuta: Boundless Trails ingame 2025-06-12 15:47
3018 01008CF01BAAC000 The Legend of Zelda Echoes of Wisdom nvdec;ASTC;intel-vendor-bug playable 2024-10-01 14:11:01
3019 0100509005AF2000 The Legend of Zelda: Breath of the Wild Demo demo ingame 2022-12-24 05:02:58
3020 01007EF00011E000 The Legend of Zelda™: Breath of the Wild gpu;amd-vendor-bug;mac-bug ingame 2024-09-23 19:35:46
3193 010000400F582000 TT Isle of Man Ride on the Edge 2 gpu;nvdec;online-broken ingame 2022-09-30 22:13:05
3194 0100752011628000 TTV2 playable 2020-11-27 13:21:36
3195 0100AFE00452E000 Tumblestone playable 2021-01-07 17:49:20
0100D1A01D7BA000 Turbo Overkill playable 2025-07-30 12:08:57
3196 010085500D5F6000 Turok gpu ingame 2021-06-04 13:16:24
3197 0100CDC00D8D6000 Turok 2: Seeds of Evil gpu;vulkan ingame 2022-09-12 17:50:05
3198 010004B0130C8000 Turrican Flashback audout playable 2021-08-30 10:07:56
3216 0100592005164000 UNBOX: Newbie's Adventure UE4 playable 2022-08-29 13:12:56
3217 01002D900C5E4000 Uncanny Valley nvdec playable 2021-06-04 13:28:45
3218 010076F011F54000 Undead & Beyond nvdec playable 2022-10-04 09:11:18
01009B700D0B8000 Undead Horde playable 2025-07-30 12:05:05
0100FC301A878000 Undead Horde 2: Necropolis playable 2025-07-30 12:06:07
3219 01008F3013E4E000 Under Leaves playable 2021-05-22 18:13:58
3220 010080B00AD66000 Undertale playable 2022-08-31 17:31:46
3221 01008F80049C6000 Unepic playable 2024-01-15 17:03:00

View File

@@ -4,20 +4,18 @@
<packageSources> <packageSources>
<clear /> <clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" /> <add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />
<add key="Ryujinx.UpdateClient" value="https://git.ryujinx.app/api/v4/projects/71/packages/nuget/index.json" /> <add key="RyubingPkgs" value="https://git.ryujinx.app/api/v4/projects/1/packages/nuget/index.json" />
</packageSources> </packageSources>
<!-- Define mappings by adding package patterns beneath the target source. -->
<!-- Ryujinx.LibHac packages will be restored from LibHacAlpha,
everything else from nuget.org. -->
<packageSourceMapping> <packageSourceMapping>
<!-- key value for <packageSource> should match key values from <packageSources> element --> <!-- key value for <packageSource> should match key values from <packageSources> element -->
<!-- These are defined and .NET still yells about multiple package sources with no mappings. Not sure what to do, this is in the docs lol -->
<packageSource key="nuget.org"> <packageSource key="nuget.org">
<package pattern="*" /> <package pattern="*" />
</packageSource> </packageSource>
<packageSource key="Ryujinx.UpdateClient">
<package pattern="Ryujinx.UpdateClient" />
<package pattern="Ryujinx.Systems.Update.Common" />
</packageSource>
<packageSource key="LibHacAlpha"> <packageSource key="LibHacAlpha">
<package pattern="Ryujinx.LibHac" /> <package pattern="Ryujinx.LibHac" />
</packageSource> </packageSource>

View File

@@ -143,12 +143,6 @@ namespace ARMeilleure.Instructions
public static void EmitCall(ArmEmitterContext context, ulong immediate) public static void EmitCall(ArmEmitterContext context, ulong immediate)
{ {
if (context.IsSingleStep)
{
context.Return(Const(immediate));
return;
}
bool isRecursive = immediate == context.EntryAddress; bool isRecursive = immediate == context.EntryAddress;
if (isRecursive) if (isRecursive)
@@ -162,25 +156,13 @@ namespace ARMeilleure.Instructions
} }
public static void EmitVirtualCall(ArmEmitterContext context, Operand target) public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
{
if (context.IsSingleStep)
{
if (target.Type == OperandType.I32)
{
target = context.ZeroExtend32(OperandType.I64, target);
}
context.Return(target);
}
else
{ {
EmitTableBranch(context, target, isJump: false); EmitTableBranch(context, target, isJump: false);
} }
}
public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn) public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
{ {
if (isReturn || context.IsSingleStep) if (isReturn)
{ {
if (target.Type == OperandType.I32) if (target.Type == OperandType.I32)
{ {

View File

@@ -3,7 +3,6 @@ using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using ExecutionContext = ARMeilleure.State.ExecutionContext;
namespace ARMeilleure.Instructions namespace ARMeilleure.Instructions
{ {
@@ -201,11 +200,7 @@ namespace ARMeilleure.Instructions
ExecutionContext context = GetContext(); ExecutionContext context = GetContext();
// If debugging, we'll handle interrupts outside
if (!Optimizations.EnableDebugging)
{
context.CheckInterrupt(); context.CheckInterrupt();
}
Statistics.ResumeTimer(); Statistics.ResumeTimer();

View File

@@ -12,7 +12,6 @@ namespace ARMeilleure
public static bool AllowLcqInFunctionTable { get; set; } = true; public static bool AllowLcqInFunctionTable { get; set; } = true;
public static bool UseUnmanagedDispatchLoop { get; set; } = true; public static bool UseUnmanagedDispatchLoop { get; set; } = true;
public static bool EnableDebugging { get; set; } = false;
public static bool UseAdvSimdIfAvailable { get; set; } = true; public static bool UseAdvSimdIfAvailable { get; set; } = true;
public static bool UseArm64AesIfAvailable { get; set; } = true; public static bool UseArm64AesIfAvailable { get; set; } = true;

View File

@@ -1,5 +1,4 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using System.Threading;
namespace ARMeilleure.State namespace ARMeilleure.State
{ {
@@ -11,7 +10,7 @@ namespace ARMeilleure.State
internal nint NativeContextPtr => _nativeContext.BasePtr; internal nint NativeContextPtr => _nativeContext.BasePtr;
internal bool Interrupted { get; private set; } private bool _interrupted;
private readonly ICounter _counter; private readonly ICounter _counter;
@@ -66,8 +65,6 @@ namespace ARMeilleure.State
public bool IsAarch32 { get; set; } public bool IsAarch32 { get; set; }
public ulong ThreadUid { get; set; }
internal ExecutionMode ExecutionMode internal ExecutionMode ExecutionMode
{ {
get get
@@ -93,19 +90,14 @@ namespace ARMeilleure.State
private readonly ExceptionCallbackNoArgs _interruptCallback; private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback; private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallbackNoArgs _stepCallback;
private readonly ExceptionCallback _supervisorCallback; private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback; private readonly ExceptionCallback _undefinedCallback;
internal int ShouldStep;
public ulong DebugPc { get; set; }
public ExecutionContext( public ExecutionContext(
IJitMemoryAllocator allocator, IJitMemoryAllocator allocator,
ICounter counter, ICounter counter,
ExceptionCallbackNoArgs interruptCallback = null, ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null, ExceptionCallback breakCallback = null,
ExceptionCallbackNoArgs stepCallback = null,
ExceptionCallback supervisorCallback = null, ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null) ExceptionCallback undefinedCallback = null)
{ {
@@ -113,7 +105,6 @@ namespace ARMeilleure.State
_counter = counter; _counter = counter;
_interruptCallback = interruptCallback; _interruptCallback = interruptCallback;
_breakCallback = breakCallback; _breakCallback = breakCallback;
_stepCallback = stepCallback;
_supervisorCallback = supervisorCallback; _supervisorCallback = supervisorCallback;
_undefinedCallback = undefinedCallback; _undefinedCallback = undefinedCallback;
@@ -136,9 +127,9 @@ namespace ARMeilleure.State
internal void CheckInterrupt() internal void CheckInterrupt()
{ {
if (Interrupted) if (_interrupted)
{ {
Interrupted = false; _interrupted = false;
_interruptCallback?.Invoke(this); _interruptCallback?.Invoke(this);
} }
@@ -148,37 +139,16 @@ namespace ARMeilleure.State
public void RequestInterrupt() public void RequestInterrupt()
{ {
Interrupted = true; _interrupted = true;
}
public void StepHandler()
{
_stepCallback?.Invoke(this);
}
public void RequestDebugStep()
{
Interlocked.Exchange(ref ShouldStep, 1);
RequestInterrupt();
} }
internal void OnBreak(ulong address, int imm) internal void OnBreak(ulong address, int imm)
{ {
if (Optimizations.EnableDebugging)
{
DebugPc = Pc;
}
_breakCallback?.Invoke(this, address, imm); _breakCallback?.Invoke(this, address, imm);
} }
internal void OnSupervisorCall(ulong address, int imm) internal void OnSupervisorCall(ulong address, int imm)
{ {
if (Optimizations.EnableDebugging)
{
DebugPc = Pc;
}
_supervisorCallback?.Invoke(this, address, imm); _supervisorCallback?.Invoke(this, address, imm);
} }

View File

@@ -22,12 +22,6 @@ namespace ARMeilleure.State
public ulong ExclusiveValueHigh; public ulong ExclusiveValueHigh;
public int Running; public int Running;
public long Tpidr2El0; public long Tpidr2El0;
/// <summary>
/// Precise PC value used for debugging.
/// This will only be set when Optimizations.EnableDebugging is true.
/// </summary>
public ulong DebugPrecisePc;
} }
private static NativeCtxStorage _dummyStorage = new(); private static NativeCtxStorage _dummyStorage = new();
@@ -45,11 +39,6 @@ namespace ARMeilleure.State
public ulong GetPc() public ulong GetPc()
{ {
if (Optimizations.EnableDebugging)
{
return GetStorage().DebugPrecisePc;
}
// TODO: More precise tracking of PC value. // TODO: More precise tracking of PC value.
return GetStorage().DispatchAddress; return GetStorage().DispatchAddress;
} }
@@ -279,11 +268,6 @@ namespace ARMeilleure.State
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running); return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
} }
public static int GetDebugPrecisePcOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
}
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target) private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
{ {
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target); return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);

View File

@@ -52,7 +52,6 @@ namespace ARMeilleure.Translation
public bool HighCq { get; } public bool HighCq { get; }
public bool HasPtc { get; } public bool HasPtc { get; }
public Aarch32Mode Mode { get; } public Aarch32Mode Mode { get; }
public bool IsSingleStep { get; }
private int _ifThenBlockStateIndex = 0; private int _ifThenBlockStateIndex = 0;
private Condition[] _ifThenBlockState = []; private Condition[] _ifThenBlockState = [];
@@ -67,8 +66,7 @@ namespace ARMeilleure.Translation
ulong entryAddress, ulong entryAddress,
bool highCq, bool highCq,
bool hasPtc, bool hasPtc,
Aarch32Mode mode, Aarch32Mode mode)
bool isSingleStep)
{ {
Memory = memory; Memory = memory;
CountTable = countTable; CountTable = countTable;
@@ -78,7 +76,6 @@ namespace ARMeilleure.Translation
HighCq = highCq; HighCq = highCq;
HasPtc = hasPtc; HasPtc = hasPtc;
Mode = mode; Mode = mode;
IsSingleStep = isSingleStep;
_labels = new Dictionary<ulong, Operand>(); _labels = new Dictionary<ulong, Operand>();
} }

View File

@@ -33,7 +33,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 7009; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 7008; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";
@@ -193,7 +193,7 @@ namespace ARMeilleure.Translation.PTC
_infosStream.Seek(0L, SeekOrigin.Begin); _infosStream.Seek(0L, SeekOrigin.Begin);
bool foundBadFunction = false; bool foundBadFunction = false;
for (int index = 0; index < _infosStream.Length / Unsafe.SizeOf<InfoEntry>(); index++) for (int index = 0; index < GetEntriesCount(); index++)
{ {
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream); InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
foreach (ulong address in blacklist) foreach (ulong address in blacklist)
@@ -303,13 +303,6 @@ namespace ARMeilleure.Translation.PTC
return false; return false;
} }
if (outerHeader.DebuggerMode != Optimizations.EnableDebugging)
{
InvalidateCompressedStream(compressedStream);
return false;
}
nint intPtr = nint.Zero; nint intPtr = nint.Zero;
try try
@@ -486,7 +479,6 @@ namespace ARMeilleure.Translation.PTC
MemoryManagerMode = GetMemoryManagerMode(), MemoryManagerMode = GetMemoryManagerMode(),
OSPlatform = GetOSPlatform(), OSPlatform = GetOSPlatform(),
Architecture = (uint)RuntimeInformation.ProcessArchitecture, Architecture = (uint)RuntimeInformation.ProcessArchitecture,
DebuggerMode = Optimizations.EnableDebugging,
UncompressedStreamSize = UncompressedStreamSize =
(long)Unsafe.SizeOf<InnerHeader>() + (long)Unsafe.SizeOf<InnerHeader>() +
@@ -1076,7 +1068,7 @@ namespace ARMeilleure.Translation.PTC
return osPlatform; return osPlatform;
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 87*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
private struct OuterHeader private struct OuterHeader
{ {
public ulong Magic; public ulong Magic;
@@ -1088,7 +1080,6 @@ namespace ARMeilleure.Translation.PTC
public byte MemoryManagerMode; public byte MemoryManagerMode;
public uint OSPlatform; public uint OSPlatform;
public uint Architecture; public uint Architecture;
public bool DebuggerMode;
public long UncompressedStreamSize; public long UncompressedStreamSize;

View File

@@ -119,25 +119,7 @@ namespace ARMeilleure.Translation
NativeInterface.RegisterThread(context, Memory, this); NativeInterface.RegisterThread(context, Memory, this);
if (Optimizations.EnableDebugging) if (Optimizations.UseUnmanagedDispatchLoop)
{
context.DebugPc = address;
do
{
if (Interlocked.CompareExchange(ref context.ShouldStep, 0, 1) == 1)
{
context.DebugPc = Step(context, context.DebugPc);
context.StepHandler();
}
else
{
context.DebugPc = ExecuteSingle(context, context.DebugPc);
}
context.CheckInterrupt();
}
while (context.Running && context.DebugPc != 0);
}
else if (Optimizations.UseUnmanagedDispatchLoop)
{ {
Stubs.DispatchLoop(context.NativeContextPtr, address); Stubs.DispatchLoop(context.NativeContextPtr, address);
} }
@@ -193,7 +175,7 @@ namespace ARMeilleure.Translation
return nextAddr; return nextAddr;
} }
private ulong Step(State.ExecutionContext context, ulong address) public ulong Step(State.ExecutionContext context, ulong address)
{ {
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true); TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
@@ -204,8 +186,6 @@ namespace ARMeilleure.Translation
return address; return address;
} }
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode) internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
{ {
if (!Functions.TryGetValue(address, out TranslatedFunction func)) if (!Functions.TryGetValue(address, out TranslatedFunction func))
@@ -249,8 +229,7 @@ namespace ARMeilleure.Translation
address, address,
highCq, highCq,
_ptc.State != PtcState.Disabled, _ptc.State != PtcState.Disabled,
mode: Aarch32Mode.User, mode: Aarch32Mode.User);
isSingleStep: singleStep);
Logger.StartPass(PassName.Decoding); Logger.StartPass(PassName.Decoding);
@@ -388,13 +367,9 @@ namespace ARMeilleure.Translation
if (block.Exit) if (block.Exit)
{ {
// Return to managed rather than tail call. // Left option here as it may be useful if we need to return to managed rather than tail call in
bool useReturns = Optimizations.EnableDebugging; // future. (eg. for debug)
bool useReturns = false;
if (Optimizations.EnableDebugging)
{
EmitDebugPrecisePcUpdate(context, block.Address);
}
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns); InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
} }
@@ -418,11 +393,6 @@ namespace ARMeilleure.Translation
} }
} }
if (Optimizations.EnableDebugging)
{
EmitDebugPrecisePcUpdate(context, opCode.Address);
}
Operand lblPredicateSkip = default; Operand lblPredicateSkip = default;
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al) if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
@@ -519,14 +489,6 @@ namespace ARMeilleure.Translation
context.MarkLabel(lblExit); context.MarkLabel(lblExit);
} }
internal static void EmitDebugPrecisePcUpdate(EmitterContext context, ulong address)
{
long debugPrecisePcOffs = NativeContext.GetDebugPrecisePcOffset();
Operand debugPrecisePcAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(debugPrecisePcOffs));
context.Store(debugPrecisePcAddr, Const(address));
}
public void InvalidateJitCacheRegion(ulong address, ulong size) public void InvalidateJitCacheRegion(ulong address, ulong size)
{ {
ulong[] overlapAddresses = []; ulong[] overlapAddresses = [];

View File

@@ -81,16 +81,16 @@ namespace Ryujinx.Audio.Renderer.Dsp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index) private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
{ {
if ((uint)index >= (uint)coefficients.Length) if ((uint)index < (uint)coefficients.Length)
{ {
return coefficients[index];
}
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}"); Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
return 0; return 0;
} }
return coefficients[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext) public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
{ {

View File

@@ -26,15 +26,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
ReadOnlySpan<float> inputBuffer, ReadOnlySpan<float> inputBuffer,
uint sampleCount) uint sampleCount)
{ {
Span<short> numeratorSpan = parameter.Numerator.AsSpan(); float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
Span<short> denominatorSpan = parameter.Denominator.AsSpan(); float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -67,15 +64,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
uint sampleCount, uint sampleCount,
float volume) float volume)
{ {
Span<short> numeratorSpan = parameter.Numerator.AsSpan(); float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
Span<short> denominatorSpan = parameter.Denominator.AsSpan(); float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -113,15 +107,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
float volume, float volume,
float ramp) float ramp)
{ {
Span<short> numeratorSpan = parameter.Numerator.AsSpan(); float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
Span<short> denominatorSpan = parameter.Denominator.AsSpan(); float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
float mixState = 0f; float mixState = 0f;
@@ -167,15 +158,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
ref BiquadFilterState state = ref states[stageIndex]; ref BiquadFilterState state = ref states[stageIndex];
Span<short> numeratorSpan = parameter.Numerator.AsSpan(); float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
Span<short> denominatorSpan = parameter.Denominator.AsSpan(); float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -213,25 +201,19 @@ namespace Ryujinx.Audio.Renderer.Dsp
uint sampleCount, uint sampleCount,
float volume) float volume)
{ {
Span<short> numerator0Span = parameter0.Numerator.AsSpan(); float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
Span<short> numerator1Span = parameter1.Numerator.AsSpan(); float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
Span<short> denominator0Span = parameter0.Denominator.AsSpan(); float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
Span<short> denominator1Span = parameter1.Denominator.AsSpan();
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter); float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter); float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter); float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter); float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter); float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter);
float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter);
float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter);
float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter);
float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter);
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -279,24 +261,19 @@ namespace Ryujinx.Audio.Renderer.Dsp
float volume, float volume,
float ramp) float ramp)
{ {
Span<short> numerator0Span = parameter0.Numerator.AsSpan(); float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
Span<short> numerator1Span = parameter1.Numerator.AsSpan(); float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
Span<short> denominator0Span = parameter0.Denominator.AsSpan(); float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
Span<short> denominator1Span = parameter1.Denominator.AsSpan();
float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter); float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter); float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter);
float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter); float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter); float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter); float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter); float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter);
float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter);
float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter);
float mixState = 0f; float mixState = 0f;

View File

@@ -40,13 +40,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
SampleRate = serverState.SampleRate; SampleRate = serverState.SampleRate;
Pitch = serverState.Pitch; Pitch = serverState.Pitch;
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
for (int i = 0; i < WaveBuffers.Length; i++) for (int i = 0; i < WaveBuffers.Length; i++)
{ {
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
} }

View File

@@ -1,6 +1,5 @@
using Ryujinx.Audio.Renderer.Parameter.Sink; using Ryujinx.Audio.Renderer.Parameter.Sink;
using Ryujinx.Audio.Renderer.Server.MemoryPool; using Ryujinx.Audio.Renderer.Server.MemoryPool;
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.Audio.Renderer.Dsp.Command namespace Ryujinx.Audio.Renderer.Dsp.Command
@@ -30,11 +29,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Input = new ushort[Constants.ChannelCountMax]; Input = new ushort[Constants.ChannelCountMax];
InputCount = parameter.InputCount; InputCount = parameter.InputCount;
Span<byte> inputSpan = parameter.Input.AsSpan();
for (int i = 0; i < InputCount; i++) for (int i = 0; i < InputCount; i++)
{ {
Input[i] = (ushort)(bufferOffset + inputSpan[i]); Input[i] = (ushort)(bufferOffset + parameter.Input[i]);
} }
CircularBuffer = circularBufferAddressInfo.GetReference(true); CircularBuffer = circularBufferAddressInfo.GetReference(true);

View File

@@ -43,13 +43,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
Span<byte> inputSpan = _parameter.Input.AsSpan();
Span<byte> outputSpan = _parameter.Output.AsSpan();
for (int i = 0; i < _parameter.ChannelCount; i++) for (int i = 0; i < _parameter.ChannelCount; i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
} }
} }
@@ -175,11 +172,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain); statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean); statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
Span<float> lastSamplesSpan = statistics.LastSamples.AsSpan();
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
{ {
lastSamplesSpan[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f)); statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f));
} }
} }
} }

View File

@@ -53,13 +53,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
SampleRate = serverState.SampleRate; SampleRate = serverState.SampleRate;
Pitch = serverState.Pitch; Pitch = serverState.Pitch;
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
for (int i = 0; i < WaveBuffers.Length; i++) for (int i = 0; i < WaveBuffers.Length; i++)
{ {
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
WaveBuffers[i] = voiceWaveBuffer.ToCommon(2); WaveBuffers[i] = voiceWaveBuffer.ToCommon(2);
} }

View File

@@ -43,13 +43,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
Span<byte> inputSpan = Parameter.Input.AsSpan();
Span<byte> outputSpan = Parameter.Output.AsSpan();
for (int i = 0; i < Parameter.ChannelCount; i++) for (int i = 0; i < Parameter.ChannelCount; i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
} }
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount); DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount);

View File

@@ -42,15 +42,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
ref VoiceUpdateState state = ref State.Span[0]; ref VoiceUpdateState state = ref State.Span[0];
Span<float> depopBuffer = DepopBuffer.Span; Span<float> depopBuffer = DepopBuffer.Span;
Span<float> lastSamplesSpan = state.LastSamples.AsSpan();
for (int i = 0; i < MixBufferCount; i++) for (int i = 0; i < MixBufferCount; i++)
{ {
if (lastSamplesSpan[i] != 0) if (state.LastSamples[i] != 0)
{ {
depopBuffer[OutputBufferIndices[i]] += lastSamplesSpan[i]; depopBuffer[OutputBufferIndices[i]] += state.LastSamples[i];
lastSamplesSpan[i] = 0; state.LastSamples[i] = 0;
} }
} }
} }

View File

@@ -35,11 +35,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputCount = sink.Parameter.InputCount; InputCount = sink.Parameter.InputCount;
InputBufferIndices = new ushort[InputCount]; InputBufferIndices = new ushort[InputCount];
Span<byte> inputSpan = sink.Parameter.Input.AsSpan();
for (int i = 0; i < Math.Min(InputCount, Constants.ChannelCountMax); i++) for (int i = 0; i < Math.Min(InputCount, Constants.ChannelCountMax); i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + sink.Parameter.Input[i]);
} }
if (sink.UpsamplerState != null) if (sink.UpsamplerState != null)

View File

@@ -38,13 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
Span<byte> inputSpan = _parameter.Input.AsSpan();
Span<byte> outputSpan = _parameter.Output.AsSpan();
for (int i = 0; i < _parameter.ChannelCount; i++) for (int i = 0; i < _parameter.ChannelCount; i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
} }
} }

View File

@@ -49,13 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
Span<byte> inputSpan = _parameter.Input.AsSpan();
Span<byte> outputSpan = _parameter.Output.AsSpan();
for (int i = 0; i < _parameter.ChannelCount; i++) for (int i = 0; i < _parameter.ChannelCount; i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
} }
} }
@@ -153,11 +150,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
{ {
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0]; ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
Span<float> inputMaxSpan = statistics.InputMax.AsSpan(); statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
Span<float> compressionGainMinSpan = statistics.CompressionGainMin.AsSpan(); statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], compressionGain);
inputMaxSpan[channelIndex] = Math.Max(inputMaxSpan[channelIndex], sampleInputMax);
compressionGainMinSpan[channelIndex] = Math.Min(compressionGainMinSpan[channelIndex], compressionGain);
} }
} }
} }

View File

@@ -79,10 +79,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public void Process(CommandList context) public void Process(CommandList context)
{ {
ref VoiceUpdateState state = ref State.Span[0];
Span<float> lastSamplesSpan = state.LastSamples.AsSpan();
for (int i = 0; i < MixBufferCount; i++) for (int i = 0; i < MixBufferCount; i++)
{ {
ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndices[i]); ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndices[i]);
@@ -91,13 +87,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float volume0 = Volume0[i]; float volume0 = Volume0[i];
float volume1 = Volume1[i]; float volume1 = Volume1[i];
ref VoiceUpdateState state = ref State.Span[0];
if (volume0 != 0 || volume1 != 0) if (volume0 != 0 || volume1 != 0)
{ {
lastSamplesSpan[i] = ProcessMixRampGrouped(outputBuffer, inputBuffer, volume0, volume1, (int)context.SampleCount); state.LastSamples[i] = ProcessMixRampGrouped(outputBuffer, inputBuffer, volume0, volume1, (int)context.SampleCount);
} }
else else
{ {
lastSamplesSpan[i] = 0; state.LastSamples[i] = 0;
} }
} }
} }

View File

@@ -43,11 +43,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
for (int i = 0; i < WaveBuffers.Length; i++) for (int i = 0; i < WaveBuffers.Length; i++)
{ {
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
} }

View File

@@ -43,11 +43,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
for (int i = 0; i < WaveBuffers.Length; i++) for (int i = 0; i < WaveBuffers.Length; i++)
{ {
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
} }

View File

@@ -66,13 +66,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
Span<byte> inputSpan = Parameter.Input.AsSpan();
Span<byte> outputSpan = Parameter.Output.AsSpan();
for (int i = 0; i < Parameter.ChannelCount; i++) for (int i = 0; i < Parameter.ChannelCount; i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
} }
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour

View File

@@ -64,13 +64,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
Span<byte> inputSpan = Parameter.Input.AsSpan();
Span<byte> outputSpan = Parameter.Output.AsSpan();
for (int i = 0; i < Parameter.ChannelCount; i++) for (int i = 0; i < Parameter.ChannelCount; i++)
{ {
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
} }
IsLongSizePreDelaySupported = isLongSizePreDelaySupported; IsLongSizePreDelaySupported = isLongSizePreDelaySupported;

View File

@@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
{ {
int tempBufferIndex = 0; int tempBufferIndex = 0;
if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) != DecodingBehaviour.SkipPitchAndSampleRateConversion) if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
{ {
voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer); voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer);
tempBufferIndex += pitchMaxLength; tempBufferIndex += pitchMaxLength;
@@ -208,7 +208,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
break; break;
} }
if ((info.DecodingBehaviour & DecodingBehaviour.PlayedSampleCountResetWhenLooping) == DecodingBehaviour.PlayedSampleCountResetWhenLooping) if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.PlayedSampleCountResetWhenLooping))
{ {
playedSampleCount = 0; playedSampleCount = 0;
} }
@@ -222,7 +222,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer[i..]); Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer[i..]);
if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) == DecodingBehaviour.SkipPitchAndSampleRateConversion) if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
{ {
for (int j = 0; j < y; j++) for (int j = 0; j < y; j++)
{ {

View File

@@ -41,12 +41,11 @@ namespace Ryujinx.Audio.Renderer.Dsp
} }
Array20<float> result = new(); Array20<float> result = new();
Span<float> resultSpan = result.AsSpan();
for (int i = 0; i < FilterBankLength; i++) for (int i = 0; i < FilterBankLength; i++)
{ {
float x = (Bank0CenterIndex - i) + offset; float x = (Bank0CenterIndex - i) + offset;
resultSpan[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f); result[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f);
} }
return result; return result;
@@ -80,9 +79,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
Debug.Assert(state.History.Length == HistoryLength); Debug.Assert(state.History.Length == HistoryLength);
Debug.Assert(bank.Length == FilterBankLength); Debug.Assert(bank.Length == FilterBankLength);
Span<float> bankSpan = bank.AsSpan();
Span<float> historySpan = state.History.AsSpan();
int curIdx = 0; int curIdx = 0;
if (Vector.IsHardwareAccelerated) if (Vector.IsHardwareAccelerated)
{ {
@@ -92,15 +88,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
while (curIdx < stopIdx) while (curIdx < stopIdx)
{ {
result += Vector.Dot( result += Vector.Dot(
new Vector<float>(bankSpan[curIdx..(curIdx + Vector<float>.Count)]), new Vector<float>(bank.AsSpan().Slice(curIdx, Vector<float>.Count)),
new Vector<float>(historySpan[curIdx..(curIdx + Vector<float>.Count)])); new Vector<float>(state.History.AsSpan().Slice(curIdx, Vector<float>.Count)));
curIdx += Vector<float>.Count; curIdx += Vector<float>.Count;
} }
} }
while (curIdx < FilterBankLength) while (curIdx < FilterBankLength)
{ {
result += bankSpan[curIdx] * historySpan[curIdx]; result += bank[curIdx] * state.History[curIdx];
curIdx++; curIdx++;
} }

View File

@@ -141,20 +141,18 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing(); bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
Span<BiquadFilterParameter> biquadFiltersSpan = voiceState.BiquadFilters.AsSpan(); if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
if (supportsOptimizedPath && biquadFiltersSpan[0].Enable && biquadFiltersSpan[1].Enable)
{ {
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)]; Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory); Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
_commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, biquadFiltersSpan, stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
} }
else else
{ {
for (int i = 0; i < biquadFiltersSpan.Length; i++) for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
{ {
ref BiquadFilterParameter filter = ref biquadFiltersSpan[i]; ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
if (filter.Enable) if (filter.Enable)
{ {
@@ -314,14 +312,11 @@ namespace Ryujinx.Audio.Renderer.Server
int nodeId = voiceState.NodeId; int nodeId = voiceState.NodeId;
uint channelsCount = voiceState.ChannelsCount; uint channelsCount = voiceState.ChannelsCount;
Span<int> channelResourceIdsSpan = voiceState.ChannelResourceIds.AsSpan();
Span<BiquadFilterParameter> biquadFiltersSpan = voiceState.BiquadFilters.AsSpan();
for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++) for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++)
{ {
Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(channelResourceIdsSpan[channelIndex]); Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(voiceState.ChannelResourceIds[channelIndex]);
ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(channelResourceIdsSpan[channelIndex]); ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(voiceState.ChannelResourceIds[channelIndex]);
PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm; PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm;
@@ -481,7 +476,7 @@ namespace Ryujinx.Audio.Renderer.Server
for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++) for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++)
{ {
voiceState.BiquadFilterNeedInitialization[i] = biquadFiltersSpan[i].Enable; voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable;
} }
} }
} }
@@ -531,19 +526,15 @@ namespace Ryujinx.Audio.Renderer.Server
if (effect.IsEnabled) if (effect.IsEnabled)
{ {
Span<float> volumesSpan = effect.Parameter.Volumes.AsSpan();
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
for (int i = 0; i < effect.Parameter.MixesCount; i++) for (int i = 0; i < effect.Parameter.MixesCount; i++)
{ {
if (volumesSpan[i] != 0.0f) if (effect.Parameter.Volumes[i] != 0.0f)
{ {
_commandBuffer.GenerateMix( _commandBuffer.GenerateMix(
(uint)bufferOffset + inputSpan[i], (uint)bufferOffset + effect.Parameter.Input[i],
(uint)bufferOffset + outputSpan[i], (uint)bufferOffset + effect.Parameter.Output[i],
nodeId, nodeId,
volumesSpan[i]); effect.Parameter.Volumes[i]);
} }
} }
} }
@@ -563,10 +554,6 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
int i = 0; int i = 0;
uint writeOffset = 0; uint writeOffset = 0;
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--) for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
{ {
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount; uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
@@ -584,8 +571,8 @@ namespace Ryujinx.Audio.Renderer.Server
_commandBuffer.GenerateAuxEffect( _commandBuffer.GenerateAuxEffect(
bufferOffset, bufferOffset,
inputSpan[i], effect.Parameter.Input[i],
outputSpan[i], effect.Parameter.Output[i],
ref effect.State, ref effect.State,
effect.IsEnabled, effect.IsEnabled,
effect.Parameter.BufferStorageSize, effect.Parameter.BufferStorageSize,
@@ -633,9 +620,6 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
Debug.Assert(effect.Type == EffectType.BiquadFilter); Debug.Assert(effect.Type == EffectType.BiquadFilter);
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
if (effect.IsEnabled) if (effect.IsEnabled)
{ {
bool needInitialization = effect.Parameter.Status == UsageState.Invalid || bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
@@ -655,8 +639,8 @@ namespace Ryujinx.Audio.Renderer.Server
(int)bufferOffset, (int)bufferOffset,
ref parameter, ref parameter,
effect.State.Slice(i, 1), effect.State.Slice(i, 1),
inputSpan[i], effect.Parameter.Input[i],
outputSpan[i], effect.Parameter.Output[i],
needInitialization, needInitialization,
nodeId); nodeId);
} }
@@ -665,8 +649,8 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
for (int i = 0; i < effect.Parameter.ChannelCount; i++) for (int i = 0; i < effect.Parameter.ChannelCount; i++)
{ {
uint inputBufferIndex = bufferOffset + inputSpan[i]; uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i];
uint outputBufferIndex = bufferOffset + outputSpan[i]; uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i];
// If the input and output isn't the same, generate a command. // If the input and output isn't the same, generate a command.
if (inputBufferIndex != outputBufferIndex) if (inputBufferIndex != outputBufferIndex)
@@ -718,8 +702,6 @@ namespace Ryujinx.Audio.Renderer.Server
int i = 0; int i = 0;
uint writeOffset = 0; uint writeOffset = 0;
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--) for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
{ {
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount; uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
@@ -737,7 +719,7 @@ namespace Ryujinx.Audio.Renderer.Server
_commandBuffer.GenerateCaptureEffect( _commandBuffer.GenerateCaptureEffect(
bufferOffset, bufferOffset,
inputSpan[i], effect.Parameter.Input[i],
effect.State.SendBufferInfo, effect.State.SendBufferInfo,
effect.IsEnabled, effect.IsEnabled,
effect.Parameter.BufferStorageSize, effect.Parameter.BufferStorageSize,

View File

@@ -218,8 +218,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>True if any biquad filter is enabled.</returns> /// <returns>True if any biquad filter is enabled.</returns>
public bool IsBiquadFilterEnabled() public bool IsBiquadFilterEnabled()
{ {
Span<BiquadFilterParameter> biquadFiltersSpan = _biquadFilters.AsSpan(); return _biquadFilters[0].Enable || _biquadFilters[1].Enable;
return biquadFiltersSpan[0].Enable || biquadFiltersSpan[1].Enable;
} }
/// <summary> /// <summary>

View File

@@ -162,11 +162,9 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
ref VoiceState currentVoiceState = ref context.GetState(i); ref VoiceState currentVoiceState = ref context.GetState(i);
Span<int> channelResourceIdsSpan = parameter.ChannelResourceIds.AsSpan();
for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++) for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++)
{ {
int channelId = channelResourceIdsSpan[channelResourceIndex]; int channelId = parameter.ChannelResourceIds[channelResourceIndex];
Debug.Assert(channelId >= 0 && channelId < context.GetCount()); Debug.Assert(channelId >= 0 && channelId < context.GetCount());

View File

@@ -126,9 +126,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
_sortedVoices.Span[i] = i; _sortedVoices.Span[i] = i;
} }
Span<int> sortedVoicesTemp = _sortedVoices[..(int)_voiceCount].Span; int[] sortedVoicesTemp = _sortedVoices[..(int)GetCount()].ToArray();
sortedVoicesTemp.Sort((a, b) => Array.Sort(sortedVoicesTemp, (a, b) =>
{ {
ref VoiceState aState = ref GetState(a); ref VoiceState aState = ref GetState(a);
ref VoiceState bState = ref GetState(b); ref VoiceState bState = ref GetState(b);
@@ -143,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
return result; return result;
}); });
// sortedVoicesTemp.CopyTo(_sortedVoices.Span); sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span);
} }
} }
} }

View File

@@ -219,17 +219,15 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// </summary> /// </summary>
private void InitializeWaveBuffers() private void InitializeWaveBuffers()
{ {
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++)
for (int i = 0; i < waveBuffersSpan.Length; i++)
{ {
waveBuffersSpan[i].StartSampleOffset = 0; WaveBuffers[i].StartSampleOffset = 0;
waveBuffersSpan[i].EndSampleOffset = 0; WaveBuffers[i].EndSampleOffset = 0;
waveBuffersSpan[i].ShouldLoop = false; WaveBuffers[i].ShouldLoop = false;
waveBuffersSpan[i].IsEndOfStream = false; WaveBuffers[i].IsEndOfStream = false;
waveBuffersSpan[i].BufferAddressInfo.Setup(0, 0); WaveBuffers[i].BufferAddressInfo.Setup(0, 0);
waveBuffersSpan[i].ContextAddressInfo.Setup(0, 0); WaveBuffers[i].ContextAddressInfo.Setup(0, 0);
waveBuffersSpan[i].IsSendToAudioProcessor = true; WaveBuffers[i].IsSendToAudioProcessor = true;
} }
} }
@@ -449,12 +447,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0]; ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0];
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++) for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
{ {
UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext); UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext);
} }
} }
@@ -539,11 +534,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <param name="context">The voice context.</param> /// <param name="context">The voice context.</param>
private void ResetResources(VoiceContext context) private void ResetResources(VoiceContext context)
{ {
Span<int> channelResourceIdsSpan = ChannelResourceIds.AsSpan();
for (int i = 0; i < ChannelsCount; i++) for (int i = 0; i < ChannelsCount; i++)
{ {
int channelResourceId = channelResourceIdsSpan[i]; int channelResourceId = ChannelResourceIds[i];
ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId); ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId);
@@ -567,11 +560,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
{ {
uint waveBufferIndex = WaveBuffersIndex; uint waveBufferIndex = WaveBuffersIndex;
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
for (int i = 0; i < waveBufferCount; i++) for (int i = 0; i < waveBufferCount; i++)
{ {
waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor = true; WaveBuffers[(int)waveBufferIndex].IsSendToAudioProcessor = true;
for (int j = 0; j < channelCount; j++) for (int j = 0; j < channelCount; j++)
{ {
@@ -600,18 +591,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
FlushWaveBufferCount = 0; FlushWaveBufferCount = 0;
} }
Span<WaveBuffer> waveBuffersSpan;
switch (PlayState) switch (PlayState)
{ {
case PlayState.Started: case PlayState.Started:
waveBuffersSpan = WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++)
for (int i = 0; i < waveBuffersSpan.Length; i++)
{ {
ref WaveBuffer waveBuffer = ref waveBuffersSpan[i]; ref WaveBuffer wavebuffer = ref WaveBuffers[i];
if (!waveBuffer.IsSendToAudioProcessor) if (!wavebuffer.IsSendToAudioProcessor)
{ {
for (int y = 0; y < ChannelsCount; y++) for (int y = 0; y < ChannelsCount; y++)
{ {
@@ -620,7 +607,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true; voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true;
} }
waveBuffer.IsSendToAudioProcessor = true; wavebuffer.IsSendToAudioProcessor = true;
} }
} }
@@ -639,13 +626,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
return false; return false;
case PlayState.Stopping: case PlayState.Stopping:
waveBuffersSpan = WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++)
for (int i = 0; i < waveBuffersSpan.Length; i++)
{ {
ref WaveBuffer waveBuffer = ref waveBuffersSpan[i]; ref WaveBuffer wavebuffer = ref WaveBuffers[i];
waveBuffer.IsSendToAudioProcessor = true; wavebuffer.IsSendToAudioProcessor = true;
for (int j = 0; j < ChannelsCount; j++) for (int j = 0; j < ChannelsCount; j++)
{ {
@@ -717,11 +702,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax]; Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
Span<int> channelResourceIdsSpan = ChannelResourceIds.AsSpan();
for (int i = 0; i < ChannelsCount; i++) for (int i = 0; i < ChannelsCount; i++)
{ {
voiceUpdateStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]); voiceUpdateStates[i] = context.GetUpdateStateForDsp(ChannelResourceIds[i]);
} }
return UpdateParametersForCommandGeneration(voiceUpdateStates); return UpdateParametersForCommandGeneration(voiceUpdateStates);

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.BuildValidationTasks
LocalesJson json; LocalesJson json;
if (isGitRunner && data.Contains("\r\n")) if (isGitRunner && data.Contains("\r\n"))
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, rebuild locally to fix..."); throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, build locally to fix...");
try try
{ {
@@ -86,7 +86,7 @@ namespace Ryujinx.BuildValidationTasks
} }
if (isGitRunner && encounteredIssue) if (isGitRunner && encounteredIssue)
throw new JsonException("1 or more locales are invalid! Rebuild locally to fix..."); throw new JsonException("1 or more locales are invalid!");
string jsonString = JsonSerializer.Serialize(json, _jsonOptions); string jsonString = JsonSerializer.Serialize(json, _jsonOptions);
@@ -102,7 +102,6 @@ namespace Ryujinx.BuildValidationTasks
struct LocalesJson struct LocalesJson
{ {
public Dictionary<string, string> Info { get; set; }
public List<string> Languages { get; set; } public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; } public List<LocalesEntry> Locales { get; set; }
} }

View File

@@ -11,17 +11,7 @@
Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;" Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;"
ConsoleToMsBuild="true" ConsoleToMsBuild="true"
Condition="'$(RuntimeIdentifier)' == ''" Condition="'$(RuntimeIdentifier)' == ''"
IgnoreExitCode="true"> />
<Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
<Output TaskParameter="ExitCode" PropertyName="BuildExitCode"/>
</Exec>
<PropertyGroup Condition=" '$(OutputOfExec.IndexOf(Unhandled exception))' != '-1'">
<ErrorOutput>$(OutputOfExec.Substring($(OutputOfExec.IndexOf("Unhandled exception"))))</ErrorOutput>
<ErrorOutput>$(ErrorOutput.Substring(0, $(ErrorOutput.IndexOf(';'))))</ErrorOutput>
</PropertyGroup>
<Error Text="$(ErrorOutput)" Condition=" '$(BuildExitCode)' != '0'"/>
</Target> </Target>
</Project> </Project>

View File

@@ -14,13 +14,12 @@ namespace Ryujinx.Common.Collections
/// Adds a new node into the tree. /// Adds a new node into the tree.
/// </summary> /// </summary>
/// <param name="node">Node to be added</param> /// <param name="node">Node to be added</param>
/// <param name="parent">Node to be added under</param>
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
public void Add(T node, T parent = null) public void Add(T node)
{ {
ArgumentNullException.ThrowIfNull(node); ArgumentNullException.ThrowIfNull(node);
Insert(node, parent); Insert(node);
} }
/// <summary> /// <summary>
@@ -77,11 +76,9 @@ namespace Ryujinx.Common.Collections
/// Inserts a new node into the tree. /// Inserts a new node into the tree.
/// </summary> /// </summary>
/// <param name="node">Node to be inserted</param> /// <param name="node">Node to be inserted</param>
/// <param name="parent">Node to be inserted under</param> private void Insert(T node)
private void Insert(T node, T parent = null)
{ {
T newNode = parent != null ? InsertWithParent(node, parent) : BSTInsert(node); T newNode = BSTInsert(node);
RestoreBalanceAfterInsertion(newNode); RestoreBalanceAfterInsertion(newNode);
} }
@@ -125,77 +122,10 @@ namespace Ryujinx.Common.Collections
else if (newNode.CompareTo(parent) < 0) else if (newNode.CompareTo(parent) < 0)
{ {
parent.Left = newNode; parent.Left = newNode;
newNode.Successor = parent;
if (parent.Predecessor != null)
{
newNode.Predecessor = parent.Predecessor;
newNode.Predecessor.Successor = newNode;
}
parent.Predecessor = newNode;
} }
else else
{ {
parent.Right = newNode; parent.Right = newNode;
newNode.Predecessor = parent;
if (parent.Successor != null)
{
newNode.Successor = parent.Successor;
newNode.Successor.Predecessor = newNode;
}
parent.Successor = newNode;
}
Count++;
return newNode;
}
/// <summary>
/// Insertion Mechanism for a Binary Search Tree (BST).
/// <br></br>
/// Inserts a new node directly under a parent node
/// where all children in the left subtree are less than <paramref name="newNode"/>,
/// and all children in the right subtree are greater than <paramref name="newNode"/>.
/// </summary>
/// <param name="newNode">Node to be inserted</param>
/// <param name="parent">Node to be inserted under</param>
/// <returns>The inserted Node</returns>
private T InsertWithParent(T newNode, T parent)
{
newNode.Parent = parent;
if (newNode.CompareTo(parent) < 0)
{
parent.Left = newNode;
newNode.Successor = parent;
if (parent.Predecessor != null)
{
newNode.Predecessor = parent.Predecessor;
parent.Predecessor = newNode;
newNode.Predecessor.Successor = newNode;
}
parent.Predecessor = newNode;
}
else
{
parent.Right = newNode;
newNode.Predecessor = parent;
if (parent.Successor != null)
{
newNode.Successor = parent.Successor;
newNode.Successor.Predecessor = newNode;
}
parent.Successor = newNode;
} }
Count++; Count++;
@@ -229,7 +159,7 @@ namespace Ryujinx.Common.Collections
} }
else else
{ {
T element = nodeToDelete.Successor; T element = Minimum(RightOf(nodeToDelete));
child = RightOf(element); child = RightOf(element);
parent = ParentOf(element); parent = ParentOf(element);
@@ -257,9 +187,6 @@ namespace Ryujinx.Common.Collections
element.Left = old.Left; element.Left = old.Left;
element.Right = old.Right; element.Right = old.Right;
element.Parent = old.Parent; element.Parent = old.Parent;
element.Predecessor = old.Predecessor;
if (element.Predecessor != null)
element.Predecessor.Successor = element;
if (ParentOf(old) == null) if (ParentOf(old) == null)
{ {
@@ -315,11 +242,6 @@ namespace Ryujinx.Common.Collections
RestoreBalanceAfterRemoval(child); RestoreBalanceAfterRemoval(child);
} }
if (old.Successor != null)
old.Successor.Predecessor = old.Predecessor;
if (old.Predecessor != null)
old.Predecessor.Successor = old.Successor;
return old; return old;
} }

View File

@@ -9,7 +9,8 @@ namespace Ryujinx.Common.Collections
public T Left; public T Left;
public T Right; public T Right;
public T Parent; public T Parent;
public T Predecessor;
public T Successor; public T Predecessor => IntrusiveRedBlackTreeImpl<T>.PredecessorOf((T)this);
public T Successor => IntrusiveRedBlackTreeImpl<T>.SuccessorOf((T)this);
} }
} }

View File

@@ -109,7 +109,7 @@ namespace Ryujinx.Common.Collections
Node<TKey, TValue> node = GetNode(key); Node<TKey, TValue> node = GetNode(key);
if (node != null) if (node != null)
{ {
Node<TKey, TValue> successor = node.Successor; Node<TKey, TValue> successor = SuccessorOf(node);
return successor != null ? successor.Key : default; return successor != null ? successor.Key : default;
} }
@@ -127,7 +127,7 @@ namespace Ryujinx.Common.Collections
Node<TKey, TValue> node = GetNode(key); Node<TKey, TValue> node = GetNode(key);
if (node != null) if (node != null)
{ {
Node<TKey, TValue> predecessor = node.Predecessor; Node<TKey, TValue> predecessor = PredecessorOf(node);
return predecessor != null ? predecessor.Key : default; return predecessor != null ? predecessor.Key : default;
} }
@@ -136,10 +136,11 @@ namespace Ryujinx.Common.Collections
} }
/// <summary> /// <summary>
/// Adds all the nodes in the dictionary as key/value pairs into a list. /// Adds all the nodes in the dictionary as key/value pairs into <paramref name="list"/>.
/// <br></br> /// <br></br>
/// The key/value pairs will be added in Level Order. /// The key/value pairs will be added in Level Order.
/// </summary> /// </summary>
/// <param name="list">List to add the tree pairs into</param>
public List<KeyValuePair<TKey, TValue>> AsLevelOrderList() public List<KeyValuePair<TKey, TValue>> AsLevelOrderList()
{ {
List<KeyValuePair<TKey, TValue>> list = []; List<KeyValuePair<TKey, TValue>> list = [];
@@ -169,7 +170,7 @@ namespace Ryujinx.Common.Collections
} }
/// <summary> /// <summary>
/// Adds all the nodes in the dictionary into a list. /// Adds all the nodes in the dictionary into <paramref name="list"/>.
/// </summary> /// </summary>
/// <returns>A list of all KeyValuePairs sorted by Key Order</returns> /// <returns>A list of all KeyValuePairs sorted by Key Order</returns>
public List<KeyValuePair<TKey, TValue>> AsList() public List<KeyValuePair<TKey, TValue>> AsList()
@@ -283,7 +284,7 @@ namespace Ryujinx.Common.Collections
} }
Node<TKey, TValue> newNode = new(key, value, parent); Node<TKey, TValue> newNode = new(key, value, parent);
if (parent == null) if (newNode.Parent == null)
{ {
Root = newNode; Root = newNode;
} }

View File

@@ -13,7 +13,6 @@ namespace Ryujinx.Common.Logging
Cpu, Cpu,
Emulation, Emulation,
FFmpeg, FFmpeg,
GdbStub,
Font, Font,
Gpu, Gpu,
Hid, Hid,

View File

@@ -1,5 +1,3 @@
using System;
namespace Ryujinx.Common.Memory namespace Ryujinx.Common.Memory
{ {
/// <summary> /// <summary>
@@ -19,10 +17,5 @@ namespace Ryujinx.Common.Memory
/// Number of elements on the array. /// Number of elements on the array.
/// </summary> /// </summary>
int Length { get; } int Length { get; }
/// <summary>
/// Number of elements on the array.
/// </summary>
public Span<T> AsSpan();
} }
} }

View File

@@ -1,7 +1,6 @@
#nullable enable #nullable enable
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -11,126 +10,11 @@ namespace Ryujinx.Common.Memory
{ {
/// <summary> /// <summary>
/// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and fast <see cref="Span{T}"/> /// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and fast <see cref="Span{T}"/>
/// accessor, with memory allocated from <see cref="ArrayPooling"/>. /// accessor, with memory allocated from <seealso cref="ArrayPool{T}.Shared"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of item to store.</typeparam> /// <typeparam name="T">The type of item to store.</typeparam>
public sealed class MemoryOwner<T> : IMemoryOwner<T> public sealed class MemoryOwner<T> : IMemoryOwner<T>
{ {
private static class ArrayPooling
{
public class Holder(T[]? array = null) : IComparable<Holder>, IComparable<int>
{
public int SkipCount;
public readonly T[]? Array = array;
public int CompareTo(Holder? other)
{
return Array!.Length.CompareTo(other!.Array!.Length);
}
public int CompareTo(int other)
{
int self = Array!.Length;
if (self < other)
{
SkipCount++;
return -1;
}
if (self > other * 4)
{
return 1;
}
return 0;
}
}
// ReSharper disable once StaticMemberInGenericType
private static int _maxCacheCount = 50;
private const int MaxSkipCount = 50;
static readonly List<Holder> _pool = new();
// ReSharper disable once StaticMemberInGenericType
static readonly Lock _lock = new();
private static int BinarySearch(List<Holder> list, int size)
{
int min = 0;
int max = list.Count-1;
while (min <= max)
{
int mid = (min + max) / 2;
int comparison = list[mid].CompareTo(size);
if (comparison == 0)
{
return mid;
}
if (comparison < 0)
{
min = mid+1;
}
else
{
max = mid-1;
}
}
return ~min;
}
public static T[] Get(int minimumSize)
{
lock (_lock)
{
int index = BinarySearch(_pool, minimumSize);
if (index >= 0)
{
Holder holder = _pool[index];
_pool.Remove(holder);
return holder.Array!;
}
return new T[minimumSize];
}
}
public static void Return(T[] array)
{
lock (_lock)
{
Holder holder = new(array);
int i = _pool.BinarySearch(holder);
if (i < 0)
{
_pool.Insert(~i, holder);
}
if (_pool.Count >= _maxCacheCount)
{
for (int index = 0; index < _pool.Count; index++)
{
Holder h = _pool[index];
if (h.SkipCount >= MaxSkipCount)
{
_pool.Remove(h);
index--;
}
}
_maxCacheCount = _pool.Count * 2;
}
}
}
}
private readonly int _length; private readonly int _length;
private T[]? _array; private T[]? _array;
@@ -141,7 +25,7 @@ namespace Ryujinx.Common.Memory
private MemoryOwner(int length) private MemoryOwner(int length)
{ {
_length = length; _length = length;
_array = ArrayPooling.Get(length); _array = ArrayPool<T>.Shared.Rent(length);
} }
/// <summary> /// <summary>
@@ -240,7 +124,7 @@ namespace Ryujinx.Common.Memory
if (array is not null) if (array is not null)
{ {
ArrayPooling.Return(array); ArrayPool<T>.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
} }
} }

View File

@@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers;
@@ -44,12 +43,9 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
{ {
// Try get a match first. // Try get a match first.
Span<int> threadIdsSpan = ThreadIds.AsSpan();
Span<T> structsSpan = Structs.AsSpan();
for (int i = 0; i < MapSize; i++) for (int i = 0; i < MapSize; i++)
{ {
int compare = Interlocked.CompareExchange(ref threadIdsSpan[i], threadId, threadId); int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, threadId);
if (compare == threadId) if (compare == threadId)
{ {
@@ -61,11 +57,11 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
for (int i = 0; i < MapSize; i++) for (int i = 0; i < MapSize; i++)
{ {
int compare = Interlocked.CompareExchange(ref threadIdsSpan[i], threadId, 0); int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, 0);
if (compare == 0) if (compare == 0)
{ {
structsSpan[i] = initial; Structs[i] = initial;
return i; return i;
} }
} }

View File

@@ -2,18 +2,8 @@ namespace Ryujinx.Common
{ {
public static class SharedConstants public static class SharedConstants
{ {
public const string DefaultLanPlayHost = "ldn.ryujinx.app"; public const string DefaultLanPlayHost = "ryuldn.vudjun.com";
public const short LanPlayPort = 30456; public const short LanPlayPort = 30456;
public const string DefaultLanPlayWebHost = DefaultLanPlayHost; public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
public const string AmiiboTagsUrl = "https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json";
public const string FaqWikiUrl = "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/FAQ-&-Troubleshooting";
public const string SetupGuideWikiUrl =
"https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Setup-&-Configuration-Guide";
public const string MultiplayerWikiUrl =
"https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Multiplayer-(LDN-Local-Wireless)-Guide";
} }
} }

View File

@@ -93,7 +93,6 @@ namespace Ryujinx.Common
//The Pokémon Franchise //The Pokémon Franchise
"0100f4300bf2c000", // New Pokémon Snap "0100f4300bf2c000", // New Pokémon Snap
"0100000011d90000", // Pokémon Brilliant Diamond "0100000011d90000", // Pokémon Brilliant Diamond
"010008c01e742000", // Pokémon Friends
"01001f5010dfa000", // Pokémon Legends: Arceus "01001f5010dfa000", // Pokémon Legends: Arceus
"01003d200baa2000", // Pokémon Mystery Dungeon - Rescue Team DX "01003d200baa2000", // Pokémon Mystery Dungeon - Rescue Team DX
"0100a3d008c5c000", // Pokémon Scarlet "0100a3d008c5c000", // Pokémon Scarlet
@@ -134,6 +133,7 @@ namespace Ryujinx.Common
"0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze "0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze
"0100ed000d390000", // Dr. Kawashima's Brain Training "0100ed000d390000", // Dr. Kawashima's Brain Training
"010067b017588000", // Endless Ocean Luminous "010067b017588000", // Endless Ocean Luminous
"0100d2f00d5c0000", // Nintendo Switch Sports
"01006b5012b32000", // Part Time UFO "01006b5012b32000", // Part Time UFO
"0100704000B3A000", // Snipperclips "0100704000B3A000", // Snipperclips
"01006a800016e000", // Super Smash Bros. Ultimate "01006a800016e000", // Super Smash Bros. Ultimate
@@ -169,8 +169,6 @@ namespace Ryujinx.Common
"010056e00853a000", // A Hat in Time "010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance "0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game "01008c2019598000", // Bluey: The Video Game
"010096f00ff22000", // Borderlands 2: Game of the Year Edition
"010007400ff24000", // Borderlands: The Pre-Sequel Ultimate Edition
"0100c6800b934000", // Brawlhalla "0100c6800b934000", // Brawlhalla
"0100dbf01000a000", // Burnout Paradise Remastered "0100dbf01000a000", // Burnout Paradise Remastered
"0100744001588000", // Cars 3: Driven to Win "0100744001588000", // Cars 3: Driven to Win
@@ -182,7 +180,6 @@ namespace Ryujinx.Common
"01001cc01b2d4000", // Goat Simulator 3 "01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2 "01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy "0100f7e00c70e000", // Hogwarts Legacy
"010013c00e930000", // Hollow Knight: Silksong
"010085500130a000", // Lego City: Undercover "010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures "010073c01af34000", // LEGO Horizon Adventures
"0100d71004694000", // Minecraft "0100d71004694000", // Minecraft
@@ -197,7 +194,6 @@ namespace Ryujinx.Common
"01008d100d43e000", // Saints Row IV "01008d100d43e000", // Saints Row IV
"0100de600beee000", // Saints Row: The Third - The Full Package "0100de600beee000", // Saints Row: The Third - The Full Package
"01001180021fa000", // Shovel Knight: Specter of Torment "01001180021fa000", // Shovel Knight: Specter of Torment
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
"0100e65002bb8000", // Stardew Valley "0100e65002bb8000", // Stardew Valley
"0100d7a01b7a2000", // Star Wars: Bounty Hunter "0100d7a01b7a2000", // Star Wars: Bounty Hunter
"0100800015926000", // Suika Game "0100800015926000", // Suika Game

View File

@@ -1,10 +0,0 @@
namespace Ryujinx.Cpu.AppleHv.Arm
{
enum ExceptionLevel : uint
{
PstateMask = 0xfffffff0,
EL1h = 0b0101,
El1t = 0b0100,
EL0 = 0b0000,
}
}

View File

@@ -11,18 +11,7 @@ namespace Ryujinx.Cpu.AppleHv
class HvExecutionContext : IExecutionContext class HvExecutionContext : IExecutionContext
{ {
/// <inheritdoc/> /// <inheritdoc/>
public ulong Pc public ulong Pc => _impl.ElrEl1;
{
get
{
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
if (currentEl == (uint)ExceptionLevel.EL1h)
{
return _impl.ElrEl1;
}
return _impl.Pc;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public long TpidrEl0 public long TpidrEl0
@@ -59,9 +48,6 @@ namespace Ryujinx.Cpu.AppleHv
set => _impl.Fpsr = value; set => _impl.Fpsr = value;
} }
/// <inheritdoc/>
public ulong ThreadUid { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public bool IsAarch32 public bool IsAarch32
{ {
@@ -81,7 +67,6 @@ namespace Ryujinx.Cpu.AppleHv
private readonly ICounter _counter; private readonly ICounter _counter;
private readonly IHvExecutionContext _shadowContext; private readonly IHvExecutionContext _shadowContext;
private IHvExecutionContext _impl; private IHvExecutionContext _impl;
private int _shouldStep;
private readonly ExceptionCallbacks _exceptionCallbacks; private readonly ExceptionCallbacks _exceptionCallbacks;
@@ -118,11 +103,6 @@ namespace Ryujinx.Cpu.AppleHv
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm); _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
} }
private void StepHandler()
{
_exceptionCallbacks.StepCallback?.Invoke(this);
}
private void SupervisorCallHandler(ulong address, int imm) private void SupervisorCallHandler(ulong address, int imm)
{ {
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm); _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
@@ -147,30 +127,6 @@ namespace Ryujinx.Cpu.AppleHv
return Interlocked.Exchange(ref _interruptRequested, 0) != 0; return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
} }
/// <inheritdoc/>
public void RequestDebugStep()
{
Interlocked.Exchange(ref _shouldStep, 1);
}
/// <inheritdoc/>
public ulong DebugPc
{
get => Pc;
set
{
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
if (currentEl == (uint)ExceptionLevel.EL1h)
{
_impl.ElrEl1 = value;
}
else
{
_impl.Pc = value;
}
}
}
/// <inheritdoc/> /// <inheritdoc/>
public void StopRunning() public void StopRunning()
{ {
@@ -186,22 +142,6 @@ namespace Ryujinx.Cpu.AppleHv
while (Running) while (Running)
{ {
if (Interlocked.CompareExchange(ref _shouldStep, 0, 1) == 1)
{
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
if (currentEl == (uint)ExceptionLevel.EL1h)
{
HvApi.hv_vcpu_get_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
spsr |= (1 << 21);
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, spsr);
}
else
{
Pstate |= (1 << 21);
}
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.MDSCR_EL1, 1);
}
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError(); HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
HvExitReason reason = vcpu.ExitInfo->Reason; HvExitReason reason = vcpu.ExitInfo->Reason;
@@ -269,20 +209,6 @@ namespace Ryujinx.Cpu.AppleHv
SupervisorCallHandler(elr - 4UL, id); SupervisorCallHandler(elr - 4UL, id);
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
break; break;
case ExceptionClass.SoftwareStepLowerEl:
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
spsr &= ~((ulong)(1 << 21));
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, spsr).ThrowOnError();
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.MDSCR_EL1, 0);
ReturnToPool(vcpu);
StepHandler();
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
break;
case ExceptionClass.BrkAarch64:
ReturnToPool(vcpu);
BreakHandler(elr, (ushort)esr);
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
break;
default: default:
throw new Exception($"Unhandled guest exception {ec}."); throw new Exception($"Unhandled guest exception {ec}.");
} }
@@ -293,8 +219,11 @@ namespace Ryujinx.Cpu.AppleHv
// TODO: Invalidate only the range that was modified? // TODO: Invalidate only the range that was modified?
return HvAddressSpace.KernelRegionTlbiEretAddress; return HvAddressSpace.KernelRegionTlbiEretAddress;
} }
else
{
return HvAddressSpace.KernelRegionEretAddress; return HvAddressSpace.KernelRegionEretAddress;
} }
}
private static void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr) private static void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr)
{ {

View File

@@ -18,8 +18,6 @@ namespace Ryujinx.Cpu.AppleHv
public bool IsAarch32 { get; set; } public bool IsAarch32 { get; set; }
public ulong ThreadUid { get; set; }
private readonly ulong[] _x; private readonly ulong[] _x;
private readonly V128[] _v; private readonly V128[] _v;
@@ -48,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
{ {
_v[index] = value; _v[index] = value;
} }
public void RequestInterrupt()
{
}
public bool GetAndClearInterruptRequested()
{
return false;
}
} }
} }

View File

@@ -2,7 +2,6 @@ using ARMeilleure.State;
using Ryujinx.Memory; using Ryujinx.Memory;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv namespace Ryujinx.Cpu.AppleHv
{ {
@@ -14,8 +13,6 @@ namespace Ryujinx.Cpu.AppleHv
private static readonly SetSimdFpReg _setSimdFpReg; private static readonly SetSimdFpReg _setSimdFpReg;
private static readonly nint _setSimdFpRegNativePtr; private static readonly nint _setSimdFpRegNativePtr;
public ulong ThreadUid { get; set; }
static HvExecutionContextVcpu() static HvExecutionContextVcpu()
{ {
// .NET does not support passing vectors by value, so we need to pass a pointer and use a native // .NET does not support passing vectors by value, so we need to pass a pointer and use a native
@@ -138,7 +135,6 @@ namespace Ryujinx.Cpu.AppleHv
} }
private readonly ulong _vcpu; private readonly ulong _vcpu;
private int _interruptRequested;
public HvExecutionContextVcpu(ulong vcpu) public HvExecutionContextVcpu(ulong vcpu)
{ {
@@ -183,17 +179,9 @@ namespace Ryujinx.Cpu.AppleHv
} }
public void RequestInterrupt() public void RequestInterrupt()
{
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
{ {
ulong vcpu = _vcpu; ulong vcpu = _vcpu;
HvApi.hv_vcpus_exit(ref vcpu, 1); HvApi.hv_vcpus_exit(ref vcpu, 1);
} }
} }
public bool GetAndClearInterruptRequested()
{
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
}
}
} }

View File

@@ -15,7 +15,6 @@ namespace Ryujinx.Cpu.AppleHv
uint Fpcr { get; set; } uint Fpcr { get; set; }
uint Fpsr { get; set; } uint Fpsr { get; set; }
ulong ThreadUid { get; set; }
ulong GetX(int index); ulong GetX(int index);
void SetX(int index, ulong value); void SetX(int index, ulong value);
@@ -40,8 +39,5 @@ namespace Ryujinx.Cpu.AppleHv
SetV(i, context.GetV(i)); SetV(i, context.GetV(i));
} }
} }
void RequestInterrupt();
bool GetAndClearInterruptRequested();
} }
} }

View File

@@ -29,11 +29,6 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
public readonly ExceptionCallback BreakCallback; public readonly ExceptionCallback BreakCallback;
/// <summary>
/// Handler for CPU software interrupts caused by single-stepping.
/// </summary>
public readonly ExceptionCallbackNoArgs StepCallback;
/// <summary> /// <summary>
/// Handler for CPU software interrupts caused by the Arm SVC instruction. /// Handler for CPU software interrupts caused by the Arm SVC instruction.
/// </summary> /// </summary>
@@ -52,19 +47,16 @@ namespace Ryujinx.Cpu
/// </remarks> /// </remarks>
/// <param name="interruptCallback">Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/></param> /// <param name="interruptCallback">Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/></param>
/// <param name="breakCallback">Handler for CPU software interrupts caused by the Arm BRK instruction</param> /// <param name="breakCallback">Handler for CPU software interrupts caused by the Arm BRK instruction</param>
/// <param name="stepCallback">Handler for CPU software interrupts caused by single-stepping</param>
/// <param name="supervisorCallback">Handler for CPU software interrupts caused by the Arm SVC instruction</param> /// <param name="supervisorCallback">Handler for CPU software interrupts caused by the Arm SVC instruction</param>
/// <param name="undefinedCallback">Handler for CPU software interrupts caused by any undefined Arm instruction</param> /// <param name="undefinedCallback">Handler for CPU software interrupts caused by any undefined Arm instruction</param>
public ExceptionCallbacks( public ExceptionCallbacks(
ExceptionCallbackNoArgs interruptCallback = null, ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null, ExceptionCallback breakCallback = null,
ExceptionCallbackNoArgs stepCallback = null,
ExceptionCallback supervisorCallback = null, ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null) ExceptionCallback undefinedCallback = null)
{ {
InterruptCallback = interruptCallback; InterruptCallback = interruptCallback;
BreakCallback = breakCallback; BreakCallback = breakCallback;
StepCallback = stepCallback;
SupervisorCallback = supervisorCallback; SupervisorCallback = supervisorCallback;
UndefinedCallback = undefinedCallback; UndefinedCallback = undefinedCallback;
} }

View File

@@ -1,6 +1,5 @@
using ARMeilleure.State; using ARMeilleure.State;
using System; using System;
using System.Threading;
namespace Ryujinx.Cpu namespace Ryujinx.Cpu
{ {
@@ -47,11 +46,6 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
bool IsAarch32 { get; set; } bool IsAarch32 { get; set; }
/// <summary>
/// Thread UID.
/// </summary>
public ulong ThreadUid { get; set; }
/// <summary> /// <summary>
/// Indicates whenever the CPU is still running code. /// Indicates whenever the CPU is still running code.
/// </summary> /// </summary>
@@ -114,23 +108,5 @@ namespace Ryujinx.Cpu
/// If you only need to pause the thread temporarily, use <see cref="RequestInterrupt"/> instead. /// If you only need to pause the thread temporarily, use <see cref="RequestInterrupt"/> instead.
/// </remarks> /// </remarks>
void StopRunning(); void StopRunning();
/// <summary>
/// Requests the thread to stop running temporarily and call <see cref="ExceptionCallbacks.InterruptCallback"/>.
/// </summary>
/// <remarks>
/// The thread might not pause immediately.
/// One must not assume that guest code is no longer being executed by the thread after calling this function.
/// After single stepping, the thread should call call <see cref="ExceptionCallbacks.StepCallback"/>.
/// </remarks>
void RequestDebugStep();
/// <summary>
/// Current Program Counter (for debugging).
/// </summary>
/// <remarks>
/// PC register for the debugger. Must not be accessed while the thread isn't stopped for debugging.
/// </remarks>
ulong DebugPc { get; set; }
} }
} }

View File

@@ -1,6 +1,5 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.State; using ARMeilleure.State;
using ExecutionContext = ARMeilleure.State.ExecutionContext;
namespace Ryujinx.Cpu.Jit namespace Ryujinx.Cpu.Jit
{ {
@@ -54,13 +53,6 @@ namespace Ryujinx.Cpu.Jit
set => _impl.IsAarch32 = value; set => _impl.IsAarch32 = value;
} }
/// <inheritdoc/>
public ulong ThreadUid
{
get => _impl.ThreadUid;
set => _impl.ThreadUid = value;
}
/// <inheritdoc/> /// <inheritdoc/>
public bool Running => _impl.Running; public bool Running => _impl.Running;
@@ -73,7 +65,6 @@ namespace Ryujinx.Cpu.Jit
counter, counter,
InterruptHandler, InterruptHandler,
BreakHandler, BreakHandler,
StepHandler,
SupervisorCallHandler, SupervisorCallHandler,
UndefinedHandler); UndefinedHandler);
@@ -102,11 +93,6 @@ namespace Ryujinx.Cpu.Jit
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm); _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
} }
private void StepHandler(ExecutionContext context)
{
_exceptionCallbacks.StepCallback?.Invoke(this);
}
private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm) private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm)
{ {
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm); _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
@@ -123,16 +109,6 @@ namespace Ryujinx.Cpu.Jit
_impl.RequestInterrupt(); _impl.RequestInterrupt();
} }
/// <inheritdoc/>
public void RequestDebugStep() => _impl.RequestDebugStep();
/// <inheritdoc/>
public ulong DebugPc
{
get => _impl.DebugPc;
set => _impl.DebugPc = value;
}
/// <inheritdoc/> /// <inheritdoc/>
public void StopRunning() public void StopRunning()
{ {

View File

@@ -1,8 +1,6 @@
using ARMeilleure;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.State; using ARMeilleure.State;
using System; using System;
using System.Threading;
namespace Ryujinx.Cpu.LightningJit.State namespace Ryujinx.Cpu.LightningJit.State
{ {
@@ -54,8 +52,6 @@ namespace Ryujinx.Cpu.LightningJit.State
public bool IsAarch32 { get; set; } public bool IsAarch32 { get; set; }
public ulong ThreadUid { get; set; }
internal ExecutionMode ExecutionMode internal ExecutionMode ExecutionMode
{ {
get get
@@ -81,20 +77,15 @@ namespace Ryujinx.Cpu.LightningJit.State
private readonly ExceptionCallbackNoArgs _interruptCallback; private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback; private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallbackNoArgs _stepCallback;
private readonly ExceptionCallback _supervisorCallback; private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback; private readonly ExceptionCallback _undefinedCallback;
internal int ShouldStep;
public ulong DebugPc { get; set; }
public ExecutionContext(IJitMemoryAllocator allocator, ICounter counter, ExceptionCallbacks exceptionCallbacks) public ExecutionContext(IJitMemoryAllocator allocator, ICounter counter, ExceptionCallbacks exceptionCallbacks)
{ {
_nativeContext = new NativeContext(allocator); _nativeContext = new NativeContext(allocator);
_counter = counter; _counter = counter;
_interruptCallback = exceptionCallbacks.InterruptCallback; _interruptCallback = exceptionCallbacks.InterruptCallback;
_breakCallback = exceptionCallbacks.BreakCallback; _breakCallback = exceptionCallbacks.BreakCallback;
_stepCallback = exceptionCallbacks.StepCallback;
_supervisorCallback = exceptionCallbacks.SupervisorCallback; _supervisorCallback = exceptionCallbacks.SupervisorCallback;
_undefinedCallback = exceptionCallbacks.UndefinedCallback; _undefinedCallback = exceptionCallbacks.UndefinedCallback;
@@ -126,17 +117,6 @@ namespace Ryujinx.Cpu.LightningJit.State
_interrupted = true; _interrupted = true;
} }
public void StepHandler()
{
_stepCallback?.Invoke(this);
}
public void RequestDebugStep()
{
Interlocked.Exchange(ref ShouldStep, 1);
RequestInterrupt();
}
internal void OnBreak(ulong address, int imm) internal void OnBreak(ulong address, int imm)
{ {
_breakCallback?.Invoke(this, address, imm); _breakCallback?.Invoke(this, address, imm);

View File

@@ -82,7 +82,15 @@ namespace Ryujinx.Graphics.Device
{ {
uint alignedOffset = index * RegisterSize; uint alignedOffset = index * RegisterSize;
return _readCallbacks[index]?.Invoke() ?? GetRefUnchecked<int>(alignedOffset); Func<int> readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (nint)index);
if (readCallback != null)
{
return readCallback();
}
else
{
return GetRefUnchecked<int>(alignedOffset);
}
} }
return 0; return 0;
@@ -97,9 +105,9 @@ namespace Ryujinx.Graphics.Device
uint alignedOffset = index * RegisterSize; uint alignedOffset = index * RegisterSize;
DebugWrite(alignedOffset, data); DebugWrite(alignedOffset, data);
SetIntAlignedUncheck(index, data); GetRefIntAlignedUncheck(index) = data;
_writeCallbacks[index]?.Invoke(data); Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (nint)index)?.Invoke(data);
} }
} }
@@ -112,9 +120,11 @@ namespace Ryujinx.Graphics.Device
uint alignedOffset = index * RegisterSize; uint alignedOffset = index * RegisterSize;
DebugWrite(alignedOffset, data); DebugWrite(alignedOffset, data);
changed = SetIntAlignedUncheckChanged(index, data); ref int storage = ref GetRefIntAlignedUncheck(index);
changed = storage != data;
storage = data;
_writeCallbacks[index]?.Invoke(data); Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (nint)index)?.Invoke(data);
} }
else else
{ {
@@ -152,24 +162,5 @@ namespace Ryujinx.Graphics.Device
{ {
return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index); return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetIntAlignedUncheck(ulong index, int data)
{
Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index) = data;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool SetIntAlignedUncheckChanged(ulong index, int data)
{
ref int val = ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index);
if (val == data)
{
return false;
}
val = data;
return true;
}
} }
} }

View File

@@ -219,14 +219,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
state.Write(LogicOpOffset, 0); state.Write(LogicOpOffset, 0);
Array8<Boolean32> enable = new(); Array8<Boolean32> enable = new();
Span<Boolean32> enableSpan = enable.AsSpan();
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
enableSpan[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1); enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1);
} }
_processor.ThreedClass.UpdateBlendEnable(enableSpan); _processor.ThreedClass.UpdateBlendEnable(ref enable);
} }
/// <summary> /// <summary>
@@ -237,14 +236,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private void UpdateColorMasks(IDeviceState state, int arg0) private void UpdateColorMasks(IDeviceState state, int arg0)
{ {
Array8<RtColorMask> masks = new(); Array8<RtColorMask> masks = new();
Span<RtColorMask> masksSpan = masks.AsSpan();
int index = 0; int index = 0;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
masksSpan[index++] = new RtColorMask((uint)arg0 & 0x1fff); masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
masksSpan[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff); masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
if (i != 3) if (i != 3)
{ {
@@ -252,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
} }
_processor.ThreedClass.UpdateColorMasks(masksSpan); _processor.ThreedClass.UpdateColorMasks(ref masks);
} }
/// <summary> /// <summary>

View File

@@ -75,12 +75,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
{ {
bool constantsMatch = true; bool constantsMatch = true;
Span<RgbHalf> blendUcodeConstantsSpan = _state.State.BlendUcodeConstants.AsSpan();
for (int i = 0; i < entry.Constants.Length; i++) for (int i = 0; i < entry.Constants.Length; i++)
{ {
RgbFloat constant = entry.Constants[i]; RgbFloat constant = entry.Constants[i];
RgbHalf constant2 = blendUcodeConstantsSpan[i]; RgbHalf constant2 = _state.State.BlendUcodeConstants[i];
if ((Half)constant.R != constant2.UnpackR() || if ((Half)constant.R != constant2.UnpackR() ||
(Half)constant.G != constant2.UnpackG() || (Half)constant.G != constant2.UnpackG() ||

View File

@@ -1,7 +1,6 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -77,11 +76,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
/// <param name="componentCount">Number of components that the format has</param> /// <param name="componentCount">Number of components that the format has</param>
public void SetVertexStride(int index, int stride, int componentCount) public void SetVertexStride(int index, int stride, int componentCount)
{ {
Span<Vector4<int>> vertexStridesSpan = _data.VertexStrides.AsSpan(); if (_data.VertexStrides[index].X != stride)
if (vertexStridesSpan[index].X != stride)
{ {
vertexStridesSpan[index].X = stride; _data.VertexStrides[index].X = stride;
MarkDirty(VertexInfoBuffer.VertexStridesOffset + index * Unsafe.SizeOf<Vector4<int>>(), sizeof(int)); MarkDirty(VertexInfoBuffer.VertexStridesOffset + index * Unsafe.SizeOf<Vector4<int>>(), sizeof(int));
} }
@@ -89,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{ {
int value = c < componentCount ? 1 : 0; int value = c < componentCount ? 1 : 0;
ref int currentValue = ref GetElementRef(ref vertexStridesSpan[index], c); ref int currentValue = ref GetElementRef(ref _data.VertexStrides[index], c);
if (currentValue != value) if (currentValue != value)
{ {
@@ -107,17 +104,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
/// <param name="divisor">If the draw is instanced, should have the vertex divisor value, otherwise should be zero</param> /// <param name="divisor">If the draw is instanced, should have the vertex divisor value, otherwise should be zero</param>
public void SetVertexOffset(int index, int offset, int divisor) public void SetVertexOffset(int index, int offset, int divisor)
{ {
Span<Vector4<int>> vertexOffsetsSpan = _data.VertexOffsets.AsSpan(); if (_data.VertexOffsets[index].X != offset)
if (vertexOffsetsSpan[index].X != offset)
{ {
vertexOffsetsSpan[index].X = offset; _data.VertexOffsets[index].X = offset;
MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf<Vector4<int>>(), sizeof(int)); MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf<Vector4<int>>(), sizeof(int));
} }
if (vertexOffsetsSpan[index].Y != divisor) if (_data.VertexOffsets[index].Y != divisor)
{ {
vertexOffsetsSpan[index].Y = divisor; _data.VertexOffsets[index].Y = divisor;
MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf<Vector4<int>>() + sizeof(int), sizeof(int)); MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf<Vector4<int>>() + sizeof(int), sizeof(int));
} }
} }

View File

@@ -125,14 +125,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
_vacContext.VertexInfoBufferUpdater.SetVertexCounts(_count, _instanceCount, _firstVertex, _firstInstance); _vacContext.VertexInfoBufferUpdater.SetVertexCounts(_count, _instanceCount, _firstVertex, _firstInstance);
_vacContext.VertexInfoBufferUpdater.SetGeometryCounts(primitivesCount); _vacContext.VertexInfoBufferUpdater.SetGeometryCounts(primitivesCount);
Span<VertexAttribState> vertexAttribStateSpan = _state.State.VertexAttribState.AsSpan();
Span<GpuVa> vertexBufferEndAddressSpan = _state.State.VertexBufferEndAddress.AsSpan();
Span<VertexBufferState> vertexBufferStateSpan = _state.State.VertexBufferState.AsSpan();
Span<Boolean32> vertexBufferInstancedSpan = _state.State.VertexBufferInstanced.AsSpan();
for (int index = 0; index < Constants.TotalVertexAttribs; index++) for (int index = 0; index < Constants.TotalVertexAttribs; index++)
{ {
VertexAttribState vertexAttrib = vertexAttribStateSpan[index]; VertexAttribState vertexAttrib = _state.State.VertexAttribState[index];
if (!FormatTable.TryGetSingleComponentAttribFormat(vertexAttrib.UnpackFormat(), out Format format, out int componentsCount)) if (!FormatTable.TryGetSingleComponentAttribFormat(vertexAttrib.UnpackFormat(), out Format format, out int componentsCount))
{ {
@@ -158,9 +153,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int bufferIndex = vertexAttrib.UnpackBufferIndex(); int bufferIndex = vertexAttrib.UnpackBufferIndex();
GpuVa endAddress = vertexBufferEndAddressSpan[bufferIndex]; GpuVa endAddress = _state.State.VertexBufferEndAddress[bufferIndex];
VertexBufferState vertexBuffer = vertexBufferStateSpan[bufferIndex]; VertexBufferState vertexBuffer = _state.State.VertexBufferState[bufferIndex];
bool instanced = vertexBufferInstancedSpan[bufferIndex]; bool instanced = _state.State.VertexBufferInstanced[bufferIndex];
ulong address = vertexBuffer.Address.Pack(); ulong address = vertexBuffer.Address.Pack();

View File

@@ -822,8 +822,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// host clipping. // host clipping.
ScreenScissorState screenScissorState = _state.State.ScreenScissorState; ScreenScissorState screenScissorState = _state.State.ScreenScissorState;
Span<ScissorState> scissorStateSpan = _state.State.ScissorState.AsSpan();
bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0; bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0;
bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0; bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0;
@@ -833,9 +831,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool fullClear = screenScissorState.X == 0 && screenScissorState.Y == 0; bool fullClear = screenScissorState.X == 0 && screenScissorState.Y == 0;
if (fullClear && clearAffectedByScissor && scissorStateSpan[0].Enable) if (fullClear && clearAffectedByScissor && _state.State.ScissorState[0].Enable)
{ {
ref ScissorState scissorState = ref scissorStateSpan[0]; ref ScissorState scissorState = ref _state.State.ScissorState[0];
fullClear = scissorState.X1 == screenScissorState.X && fullClear = scissorState.X1 == screenScissorState.X &&
scissorState.Y1 == screenScissorState.Y && scissorState.Y1 == screenScissorState.Y &&
@@ -894,9 +892,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int scissorW = screenScissorState.Width; int scissorW = screenScissorState.Width;
int scissorH = screenScissorState.Height; int scissorH = screenScissorState.Height;
if (clearAffectedByScissor && scissorStateSpan[0].Enable) if (clearAffectedByScissor && _state.State.ScissorState[0].Enable)
{ {
ref ScissorState scissorState = ref scissorStateSpan[0]; ref ScissorState scissorState = ref _state.State.ScissorState[0];
scissorX = Math.Max(scissorX, scissorState.X1); scissorX = Math.Max(scissorX, scissorState.X1);
scissorY = Math.Max(scissorY, scissorState.Y1); scissorY = Math.Max(scissorY, scissorState.Y1);

View File

@@ -3,7 +3,6 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
namespace Ryujinx.Graphics.Gpu.Engine.Threed namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
@@ -215,11 +214,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates the type of the vertex attributes consumed by the shader. /// Updates the type of the vertex attributes consumed by the shader.
/// </summary> /// </summary>
/// <param name="state">The new state</param> /// <param name="state">The new state</param>
public void SetAttributeTypes(ReadOnlySpan<VertexAttribState> state) public void SetAttributeTypes(ref Array32<VertexAttribState> state)
{ {
bool changed = false; bool changed = false;
// ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes; ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
Span<AttributeType> attributeTypesSpan = _graphics.AttributeTypes.AsSpan();
bool mayConvertVtgToCompute = ShaderCache.MayConvertVtgToCompute(ref _context.Capabilities); bool mayConvertVtgToCompute = ShaderCache.MayConvertVtgToCompute(ref _context.Capabilities);
bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats && !mayConvertVtgToCompute; bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats && !mayConvertVtgToCompute;
@@ -263,9 +261,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
} }
if (attributeTypesSpan[location] != value) if (attributeTypes[location] != value)
{ {
attributeTypesSpan[location] = value; attributeTypes[location] = value;
changed = true; changed = true;
} }
} }
@@ -281,13 +279,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
/// <param name="rtControl">The render target control register</param> /// <param name="rtControl">The render target control register</param>
/// <param name="state">The color attachment state</param> /// <param name="state">The color attachment state</param>
public void SetFragmentOutputTypes(RtControl rtControl, ReadOnlySpan<RtColorState> state) public void SetFragmentOutputTypes(RtControl rtControl, ref Array8<RtColorState> state)
{ {
bool changed = false; bool changed = false;
int count = rtControl.UnpackCount(); int count = rtControl.UnpackCount();
Span<AttributeType> fragmentOutputTypesSpan = _graphics.FragmentOutputTypes.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
int rtIndex = rtControl.UnpackPermutationIndex(index); int rtIndex = rtControl.UnpackPermutationIndex(index);
@@ -300,9 +296,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float; AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float;
if (type != fragmentOutputTypesSpan[index]) if (type != _graphics.FragmentOutputTypes[index])
{ {
fragmentOutputTypesSpan[index] = type; _graphics.FragmentOutputTypes[index] = type;
changed = true; changed = true;
} }
} }

View File

@@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (index < BlockSize) if (index < BlockSize)
{ {
int groupIndex = _registerToGroupMapping[index]; int groupIndex = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_registerToGroupMapping), (nint)index);
if (groupIndex != 0) if (groupIndex != 0)
{ {
groupIndex--; groupIndex--;

View File

@@ -423,11 +423,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
private void UpdateTfBufferState() private void UpdateTfBufferState()
{ {
Span<TfBufferState> tfBufferStateSpan = _state.State.TfBufferState.AsSpan();
for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
{ {
TfBufferState tfb = tfBufferStateSpan[index]; TfBufferState tfb = _state.State.TfBufferState[index];
if (!tfb.Enable) if (!tfb.Enable)
{ {
@@ -468,10 +466,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
MemoryManager memoryManager = _channel.MemoryManager; MemoryManager memoryManager = _channel.MemoryManager;
RtControl rtControl = _state.State.RtControl; RtControl rtControl = _state.State.RtControl;
bool useControl = (updateFlags & RenderTargetUpdateFlags.UseControl) == RenderTargetUpdateFlags.UseControl; bool useControl = updateFlags.HasFlag(RenderTargetUpdateFlags.UseControl);
bool layered = (updateFlags & RenderTargetUpdateFlags.Layered) == RenderTargetUpdateFlags.Layered; bool layered = updateFlags.HasFlag(RenderTargetUpdateFlags.Layered);
bool singleColor = (updateFlags & RenderTargetUpdateFlags.SingleColor) == RenderTargetUpdateFlags.SingleColor; bool singleColor = updateFlags.HasFlag(RenderTargetUpdateFlags.SingleColor);
bool discard = (updateFlags & RenderTargetUpdateFlags.DiscardClip) == RenderTargetUpdateFlags.DiscardClip; bool discard = updateFlags.HasFlag(RenderTargetUpdateFlags.DiscardClip);
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets; int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
@@ -489,13 +487,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool changedScale = false; bool changedScale = false;
uint rtNoAlphaMask = 0; uint rtNoAlphaMask = 0;
Span<RtColorState> rtColorStateSpan = _state.State.RtColorState.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index; int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
RtColorState colorState = rtColorStateSpan[rtIndex]; RtColorState colorState = _state.State.RtColorState[rtIndex];
if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse)) if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse))
{ {
@@ -543,7 +539,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Image.Texture depthStencil = null; Image.Texture depthStencil = null;
if (dsEnable && (updateFlags & RenderTargetUpdateFlags.UpdateDepthStencil) == RenderTargetUpdateFlags.UpdateDepthStencil) if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil))
{ {
RtDepthStencilState dsState = _state.State.RtDepthStencilState; RtDepthStencilState dsState = _state.State.RtDepthStencilState;
Size3D dsSize = _state.State.RtDepthStencilSize; Size3D dsSize = _state.State.RtDepthStencilSize;
@@ -603,7 +599,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
public void UpdateRenderTargetSpecialization() public void UpdateRenderTargetSpecialization()
{ {
_currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, _state.State.RtColorState.AsSpan()); _currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, ref _state.State.RtColorState);
} }
/// <summary> /// <summary>
@@ -628,11 +624,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
const int MaxH = 0xffff; const int MaxH = 0xffff;
Span<Rectangle<int>> regions = stackalloc Rectangle<int>[Constants.TotalViewports]; Span<Rectangle<int>> regions = stackalloc Rectangle<int>[Constants.TotalViewports];
Span<ScissorState> scissorStateSpan = _state.State.ScissorState.AsSpan();
for (int index = 0; index < Constants.TotalViewports; index++) for (int index = 0; index < Constants.TotalViewports; index++)
{ {
ScissorState scissor = scissorStateSpan[index]; ScissorState scissor = _state.State.ScissorState[index];
bool enable = scissor.Enable && (scissor.X1 != MinX || bool enable = scissor.Enable && (scissor.X1 != MinX ||
scissor.Y1 != MinY || scissor.Y1 != MinY ||
@@ -736,8 +731,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateDepthMode(); UpdateDepthMode();
Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports]; Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
Span<ViewportTransform> viewportTransformSpan = _state.State.ViewportTransform.AsSpan();
Span<ViewportExtents> viewportExtentsSpan = _state.State.ViewportExtents.AsSpan();
for (int index = 0; index < Constants.TotalViewports; index++) for (int index = 0; index < Constants.TotalViewports; index++)
{ {
@@ -752,8 +745,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
continue; continue;
} }
ref ViewportTransform transform = ref viewportTransformSpan[index]; ref ViewportTransform transform = ref _state.State.ViewportTransform[index];
ref ViewportExtents extents = ref viewportExtentsSpan[index]; ref ViewportExtents extents = ref _state.State.ViewportExtents[index];
float scaleX = MathF.Abs(transform.ScaleX); float scaleX = MathF.Abs(transform.ScaleX);
float scaleY = transform.ScaleY; float scaleY = transform.ScaleY;
@@ -975,11 +968,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
uint vbEnableMask = _vbEnableMask; uint vbEnableMask = _vbEnableMask;
Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs]; Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
Span<VertexAttribState> vertexAttribStateSpan = _state.State.VertexAttribState.AsSpan();
for (int index = 0; index < Constants.TotalVertexAttribs; index++) for (int index = 0; index < Constants.TotalVertexAttribs; index++)
{ {
VertexAttribState vertexAttrib = vertexAttribStateSpan[index]; VertexAttribState vertexAttrib = _state.State.VertexAttribState[index];
int bufferIndex = vertexAttrib.UnpackBufferIndex(); int bufferIndex = vertexAttrib.UnpackBufferIndex();
@@ -1023,7 +1015,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_pipeline.SetVertexAttribs(vertexAttribs); _pipeline.SetVertexAttribs(vertexAttribs);
_context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs); _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
_currentSpecState.SetAttributeTypes(_state.State.VertexAttribState.AsSpan()); _currentSpecState.SetAttributeTypes(ref _state.State.VertexAttribState);
} }
/// <summary> /// <summary>
@@ -1122,24 +1114,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int drawVertexCount = _drawState.DrawVertexCount; int drawVertexCount = _drawState.DrawVertexCount;
uint vbEnableMask = 0; uint vbEnableMask = 0;
Span<VertexBufferState> vertexBufferStateSpan = _state.State.VertexBufferState.AsSpan();
Span<BufferPipelineDescriptor> vertexBuffersSpan = _pipeline.VertexBuffers.AsSpan();
Span<GpuVa> vertexBufferEndAddressSpan = _state.State.VertexBufferEndAddress.AsSpan();
Span<Boolean32> vertexBufferInstancedSpan = _state.State.VertexBufferInstanced.AsSpan();
for (int index = 0; index < Constants.TotalVertexBuffers; index++) for (int index = 0; index < Constants.TotalVertexBuffers; index++)
{ {
VertexBufferState vertexBuffer = vertexBufferStateSpan[index]; VertexBufferState vertexBuffer = _state.State.VertexBufferState[index];
if (!vertexBuffer.UnpackEnable()) if (!vertexBuffer.UnpackEnable())
{ {
vertexBuffersSpan[index] = new BufferPipelineDescriptor(false, 0, 0); _pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(false, 0, 0);
_channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0); _channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
continue; continue;
} }
GpuVa endAddress = vertexBufferEndAddressSpan[index]; GpuVa endAddress = _state.State.VertexBufferEndAddress[index];
ulong address = vertexBuffer.Address.Pack(); ulong address = vertexBuffer.Address.Pack();
@@ -1150,7 +1137,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int stride = vertexBuffer.UnpackStride(); int stride = vertexBuffer.UnpackStride();
bool instanced = vertexBufferInstancedSpan[index]; bool instanced = _state.State.VertexBufferInstanced[index];
int divisor = instanced ? vertexBuffer.Divisor : 0; int divisor = instanced ? vertexBuffer.Divisor : 0;
@@ -1197,7 +1184,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
size = Math.Min(vbSize, (ulong)((firstInstance + drawFirstVertex + drawVertexCount) * stride)); size = Math.Min(vbSize, (ulong)((firstInstance + drawFirstVertex + drawVertexCount) * stride));
} }
vertexBuffersSpan[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor); _pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
_channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor); _channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
} }
@@ -1250,12 +1237,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool rtColorMaskShared = _state.State.RtColorMaskShared; bool rtColorMaskShared = _state.State.RtColorMaskShared;
Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets]; Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets];
Span<RtColorMask> rtColorMaskSpan = _state.State.RtColorMask.AsSpan();
Span<uint> colorWriteMaskSpan = _pipeline.ColorWriteMask.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
RtColorMask colorMask = rtColorMaskSpan[rtColorMaskShared ? 0 : index]; RtColorMask colorMask = _state.State.RtColorMask[rtColorMaskShared ? 0 : index];
uint componentMask; uint componentMask;
@@ -1265,7 +1250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u); componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
componentMasks[index] = componentMask; componentMasks[index] = componentMask;
colorWriteMaskSpan[index] = componentMask; _pipeline.ColorWriteMask[index] = componentMask;
} }
_context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks); _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks);
@@ -1297,14 +1282,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (blendIndependent) if (blendIndependent)
{ {
Span<Boolean32> blendEnableSpan = _state.State.BlendEnable.AsSpan();
Span<BlendState> blendStateSpan = _state.State.BlendState.AsSpan();
Span<BlendDescriptor> blendDescriptorsSpan = _pipeline.BlendDescriptors.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
bool enable = blendEnableSpan[index]; bool enable = _state.State.BlendEnable[index];
BlendState blend = blendStateSpan[index]; BlendState blend = _state.State.BlendState[index];
BlendDescriptor descriptor = new( BlendDescriptor descriptor = new(
enable, enable,
@@ -1325,7 +1306,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
dualSourceBlendEnabled = true; dualSourceBlendEnabled = true;
} }
blendDescriptorsSpan[index] = descriptor; _pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
} }
@@ -1353,11 +1334,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
dualSourceBlendEnabled = true; dualSourceBlendEnabled = true;
} }
Span<BlendDescriptor> blendDescriptorsSpan = _pipeline.BlendDescriptors.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
blendDescriptorsSpan[index] = descriptor; _pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
} }
@@ -1443,13 +1422,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ShaderAddresses addresses = new(); ShaderAddresses addresses = new();
Span<ulong> addressesSpan = addresses.AsSpan(); Span<ulong> addressesSpan = addresses.AsSpan();
Span<ShaderState> shaderStateSpan = _state.State.ShaderState.AsSpan();
ulong baseAddress = _state.State.ShaderBaseAddress.Pack(); ulong baseAddress = _state.State.ShaderBaseAddress.Pack();
for (int index = 0; index < 6; index++) for (int index = 0; index < 6; index++)
{ {
ShaderState shader = shaderStateSpan[index]; ShaderState shader = _state.State.ShaderState[index];
if (!shader.UnpackEnable() && index != 1) if (!shader.UnpackEnable() && index != 1)
{ {
continue; continue;

View File

@@ -242,22 +242,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="rhs">Second struct</param> /// <param name="rhs">Second struct</param>
/// <returns>True if equal, false otherwise</returns> /// <returns>True if equal, false otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool UnsafeEquals32Byte<T>(Span<T> lhs, Span<T> rhs) where T : unmanaged private static bool UnsafeEquals32Byte<T>(ref T lhs, ref T rhs) where T : unmanaged
{ {
if (Vector256.IsHardwareAccelerated) if (Vector256.IsHardwareAccelerated)
{ {
return Vector256.EqualsAll( return Vector256.EqualsAll(
Unsafe.As<Span<T>, ReadOnlySpan<Vector256<uint>>>(ref lhs)[0], Unsafe.As<T, Vector256<uint>>(ref lhs),
Unsafe.As<Span<T>, ReadOnlySpan<Vector256<uint>>>(ref rhs)[0] Unsafe.As<T, Vector256<uint>>(ref rhs)
); );
} }
else else
{ {
ReadOnlySpan<Vector128<uint>> lhsVec = Unsafe.As<Span<T>, ReadOnlySpan<Vector128<uint>>>(ref lhs); ref Vector128<uint> lhsVec = ref Unsafe.As<T, Vector128<uint>>(ref lhs);
ReadOnlySpan<Vector128<uint>> rhsVec = Unsafe.As<Span<T>, ReadOnlySpan<Vector128<uint>>>(ref rhs); ref Vector128<uint> rhsVec = ref Unsafe.As<T, Vector128<uint>>(ref rhs);
return Vector128.EqualsAll(lhsVec[0], rhsVec[0]) && return Vector128.EqualsAll(lhsVec, rhsVec) &&
Vector128.EqualsAll(lhsVec[1], rhsVec[1]); Vector128.EqualsAll(Unsafe.Add(ref lhsVec, 1), Unsafe.Add(ref rhsVec, 1));
} }
} }
@@ -265,26 +265,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates blend enable. Respects current shadow mode. /// Updates blend enable. Respects current shadow mode.
/// </summary> /// </summary>
/// <param name="masks">Blend enable</param> /// <param name="masks">Blend enable</param>
public void UpdateBlendEnable(Span<Boolean32> enable) public void UpdateBlendEnable(ref Array8<Boolean32> enable)
{ {
SetMmeShadowRamControlMode shadow = ShadowMode; SetMmeShadowRamControlMode shadow = ShadowMode;
Span<Boolean32> state = _state.State.BlendEnable.AsSpan(); ref Array8<Boolean32> state = ref _state.State.BlendEnable;
if (shadow.IsReplay()) if (shadow.IsReplay())
{ {
state.CopyTo(enable); enable = _state.ShadowState.BlendEnable;
} }
if (!UnsafeEquals32Byte(enable, state)) if (!UnsafeEquals32Byte(ref enable, ref state))
{ {
enable.CopyTo(state); state = enable;
_stateUpdater.ForceDirty(StateUpdater.BlendStateIndex); _stateUpdater.ForceDirty(StateUpdater.BlendStateIndex);
} }
if (shadow.IsTrack()) if (shadow.IsTrack())
{ {
enable.CopyTo(state); _state.ShadowState.BlendEnable = enable;
} }
} }
@@ -292,26 +292,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates color masks. Respects current shadow mode. /// Updates color masks. Respects current shadow mode.
/// </summary> /// </summary>
/// <param name="masks">Color masks</param> /// <param name="masks">Color masks</param>
public void UpdateColorMasks(Span<RtColorMask> masks) public void UpdateColorMasks(ref Array8<RtColorMask> masks)
{ {
SetMmeShadowRamControlMode shadow = ShadowMode; SetMmeShadowRamControlMode shadow = ShadowMode;
Span<RtColorMask> state = _state.State.RtColorMask.AsSpan(); ref Array8<RtColorMask> state = ref _state.State.RtColorMask;
if (shadow.IsReplay()) if (shadow.IsReplay())
{ {
state.CopyTo(masks); masks = _state.ShadowState.RtColorMask;
} }
if (!UnsafeEquals32Byte(masks, state)) if (!UnsafeEquals32Byte(ref masks, ref state))
{ {
masks.CopyTo(state); state = masks;
_stateUpdater.ForceDirty(StateUpdater.RtColorMaskIndex); _stateUpdater.ForceDirty(StateUpdater.RtColorMaskIndex);
} }
if (shadow.IsTrack()) if (shadow.IsTrack())
{ {
masks.CopyTo(state); _state.ShadowState.RtColorMask = masks;
} }
} }

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others. /// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
/// </summary> /// </summary>
class Buffer : INonOverlappingRange, ISyncActionHandler, IDisposable class Buffer : IRange, ISyncActionHandler, IDisposable
{ {
private const ulong GranularBufferThreshold = 4096; private const ulong GranularBufferThreshold = 4096;
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Size of the buffer in bytes. /// Size of the buffer in bytes.
/// </summary> /// </summary>
public ulong Size { get; private set; } public ulong Size { get; }
/// <summary> /// <summary>
/// End address of the buffer in guest memory. /// End address of the buffer in guest memory.
@@ -60,13 +60,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks> /// <remarks>
/// This is null until at least one modification occurs. /// This is null until at least one modification occurs.
/// </remarks> /// </remarks>
private BufferModifiedRangeList _modifiedRanges; private BufferModifiedRangeList _modifiedRanges = null;
/// <summary> /// <summary>
/// A structure that is used to flush buffer data back to a host mapped buffer for cached readback. /// A structure that is used to flush buffer data back to a host mapped buffer for cached readback.
/// Only used if the buffer data is explicitly owned by device local memory. /// Only used if the buffer data is explicitly owned by device local memory.
/// </summary> /// </summary>
private BufferPreFlush _preFlush; private BufferPreFlush _preFlush = null;
/// <summary> /// <summary>
/// Usage tracking state that determines what type of backing the buffer should use. /// Usage tracking state that determines what type of backing the buffer should use.
@@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong size, ulong size,
BufferStage stage, BufferStage stage,
bool sparseCompatible, bool sparseCompatible,
RangeItem<Buffer>[] baseBuffers) IEnumerable<Buffer> baseBuffers = null)
{ {
_context = context; _context = context;
_physicalMemory = physicalMemory; _physicalMemory = physicalMemory;
@@ -126,22 +126,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
_useGranular = size > GranularBufferThreshold; _useGranular = size > GranularBufferThreshold;
List<IRegionHandle> baseHandles = null; IEnumerable<IRegionHandle> baseHandles = null;
if (baseBuffers.Length != 0) if (baseBuffers != null)
{ {
baseHandles = new List<IRegionHandle>(); baseHandles = baseBuffers.SelectMany(buffer =>
foreach (RangeItem<Buffer> item in baseBuffers)
{ {
if (item.Value._useGranular) if (buffer._useGranular)
{ {
baseHandles.AddRange((item.Value._memoryTrackingGranular.GetHandles())); return buffer._memoryTrackingGranular.GetHandles();
} }
else else
{ {
baseHandles.Add(item.Value._memoryTracking); return Enumerable.Repeat(buffer._memoryTracking, 1);
}
} }
});
} }
if (_useGranular) if (_useGranular)
@@ -172,9 +171,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
_memoryTracking.RegisterPreciseAction(PreciseAction); _memoryTracking.RegisterPreciseAction(PreciseAction);
} }
_externalFlushDelegate = ExternalFlush; _externalFlushDelegate = new RegionSignal(ExternalFlush);
_loadDelegate = LoadRegion; _loadDelegate = new Action<ulong, ulong>(LoadRegion);
_modifiedDelegate = RegionModified; _modifiedDelegate = new Action<ulong, ulong>(RegionModified);
_virtualDependenciesLock = new ReaderWriterLockSlim(); _virtualDependenciesLock = new ReaderWriterLockSlim();
} }
@@ -248,11 +247,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return Address < address + size && address < EndAddress; return Address < address + size && address < EndAddress;
} }
public INonOverlappingRange Split(ulong splitAddress)
{
throw new NotImplementedException();
}
/// <summary> /// <summary>
/// Checks if a given range is fully contained in the buffer. /// Checks if a given range is fully contained in the buffer.
/// </summary> /// </summary>
@@ -441,7 +435,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="from">The buffer to inherit from</param> /// <param name="from">The buffer to inherit from</param>
public void InheritModifiedRanges(Buffer from) public void InheritModifiedRanges(Buffer from)
{ {
if (from._modifiedRanges is { HasRanges: true }) if (from._modifiedRanges != null && from._modifiedRanges.HasRanges)
{ {
if (from._syncActionRegistered && !_syncActionRegistered) if (from._syncActionRegistered && !_syncActionRegistered)
{ {
@@ -449,7 +443,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_syncActionRegistered = true; _syncActionRegistered = true;
} }
void RegisterRangeAction(ulong address, ulong size) void registerRangeAction(ulong address, ulong size)
{ {
if (_useGranular) if (_useGranular)
{ {
@@ -463,7 +457,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
EnsureRangeList(); EnsureRangeList();
_modifiedRanges.InheritRanges(from._modifiedRanges, RegisterRangeAction); _modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction);
} }
if (from._dirtyStart != ulong.MaxValue) if (from._dirtyStart != ulong.MaxValue)
@@ -505,7 +499,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
// Cut off the start. // Cut off the start.
_dirtyStart = end < _dirtyEnd ? end : ulong.MaxValue; if (end < _dirtyEnd)
{
_dirtyStart = end;
}
else
{
_dirtyStart = ulong.MaxValue;
}
} }
else if (end >= _dirtyEnd) else if (end >= _dirtyEnd)
{ {

View File

@@ -1,5 +1,4 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Memory.Range;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -57,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="parent">Parent buffer</param> /// <param name="parent">Parent buffer</param>
/// <param name="stage">Initial buffer stage</param> /// <param name="stage">Initial buffer stage</param>
/// <param name="baseBuffers">Buffers to inherit state from</param> /// <param name="baseBuffers">Buffers to inherit state from</param>
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, RangeItem<Buffer>[] baseBuffers) public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, IEnumerable<Buffer> baseBuffers = null)
{ {
_size = (int)parent.Size; _size = (int)parent.Size;
_systemMemoryType = context.Capabilities.MemoryType; _systemMemoryType = context.Capabilities.MemoryType;
@@ -73,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferStage storageFlags = stage & BufferStage.StorageMask; BufferStage storageFlags = stage & BufferStage.StorageMask;
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers.Length == 0) if (parent.Size > DeviceLocalSizeThreshold && baseBuffers == null)
{ {
_desiredType = BufferBackingType.DeviceMemory; _desiredType = BufferBackingType.DeviceMemory;
} }
@@ -101,11 +100,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
// TODO: Might be nice to force atomic access to be device local for any stage. // TODO: Might be nice to force atomic access to be device local for any stage.
} }
if (baseBuffers.Length != 0) if (baseBuffers != null)
{ {
foreach (RangeItem<Buffer> item in baseBuffers) foreach (Buffer buffer in baseBuffers)
{ {
CombineState(item.Value.BackingState); CombineState(buffer.BackingState);
} }
} }
} }

View File

@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@@ -38,9 +39,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Only modified from the GPU thread. Must lock for add/remove. /// Only modified from the GPU thread. Must lock for add/remove.
/// Must lock for any access from other threads. /// Must lock for any access from other threads.
/// </remarks> /// </remarks>
private readonly NonOverlappingRangeList<Buffer> _buffers; private readonly RangeList<Buffer> _buffers;
private readonly MultiRangeList<MultiRangeBuffer> _multiRangeBuffers; private readonly MultiRangeList<MultiRangeBuffer> _multiRangeBuffers;
private Buffer[] _bufferOverlaps;
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache; private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache; private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
private bool _pruneCaches; private bool _pruneCaches;
@@ -61,6 +64,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_buffers = []; _buffers = [];
_multiRangeBuffers = []; _multiRangeBuffers = [];
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
_dirtyCache = new Dictionary<ulong, BufferCacheEntry>(); _dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
// There are a lot more entries on the modified cache, so it is separate from the one for ForceDirty. // There are a lot more entries on the modified cache, so it is separate from the one for ForceDirty.
@@ -74,21 +79,24 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="e">Event arguments</param> /// <param name="e">Event arguments</param>
public void MemoryUnmappedHandler(object sender, UnmapEventArgs e) public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
{ {
Buffer[] overlaps = new Buffer[10];
int overlapCount;
MultiRange range = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size); MultiRange range = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
for (int index = 0; index < range.Count; index++) for (int index = 0; index < range.Count; index++)
{ {
MemoryRange subRange = range.GetSubRange(index); MemoryRange subRange = range.GetSubRange(index);
_buffers.Lock.EnterReadLock(); lock (_buffers)
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
for (int i = 0; i < overlaps.Length; i++)
{ {
overlaps[i].Value.Unmapped(subRange.Address, subRange.Size); overlapCount = _buffers.FindOverlaps(subRange.Address, subRange.Size, ref overlaps);
} }
_buffers.Lock.ExitReadLock(); for (int i = 0; i < overlapCount; i++)
{
overlaps[i].Unmapped(subRange.Address, subRange.Size);
}
} }
} }
@@ -129,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>Physical ranges of the buffer, after address translation</returns> /// <returns>Physical ranges of the buffer, after address translation</returns>
public MultiRange TranslateAndCreateMultiBuffers(MemoryManager memoryManager, ulong gpuVa, ulong size, BufferStage stage) public MultiRange TranslateAndCreateMultiBuffers(MemoryManager memoryManager, ulong gpuVa, ulong size, BufferStage stage)
{ {
if (gpuVa == 0 || size == 0) if (gpuVa == 0)
{ {
return new MultiRange(MemoryManager.PteUnmapped, size); return new MultiRange(MemoryManager.PteUnmapped, size);
} }
@@ -328,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask; ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
ulong alignedSize = alignedEndAddress - alignedAddress; ulong alignedSize = alignedEndAddress - alignedAddress;
Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize).Value; Buffer buffer = _buffers.FindFirstOverlap(alignedAddress, alignedSize);
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false); BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize); alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
@@ -395,7 +403,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (subRange.Address != MemoryManager.PteUnmapped) if (subRange.Address != MemoryManager.PteUnmapped)
{ {
Buffer buffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value; Buffer buffer = _buffers.FindFirstOverlap(subRange.Address, subRange.Size);
virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size); virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size);
physicalBuffers.Add(buffer); physicalBuffers.Add(buffer);
@@ -487,12 +495,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param> /// <param name="stage">The type of usage that created the buffer</param>
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage) private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
{ {
Buffer newBuffer = null; Buffer[] overlaps = _bufferOverlaps;
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
_buffers.Lock.EnterWriteLock(); if (overlapsCount != 0)
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
if (overlaps.Length != 0)
{ {
// The buffer already exists. We can just return the existing buffer // The buffer already exists. We can just return the existing buffer
// if the buffer we need is fully contained inside the overlapping buffer. // if the buffer we need is fully contained inside the overlapping buffer.
@@ -501,8 +507,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
// old buffer(s) to the new buffer. // old buffer(s) to the new buffer.
ulong endAddress = address + size; ulong endAddress = address + size;
Buffer overlap0 = overlaps[0];
if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress) if (overlap0.Address > address || overlap0.EndAddress < endAddress)
{ {
bool anySparseCompatible = false; bool anySparseCompatible = false;
@@ -515,62 +522,55 @@ namespace Ryujinx.Graphics.Gpu.Memory
// sequential memory. // sequential memory.
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the // Allowing for 2 pages (rather than just one) is necessary to catch cases where the
// range crosses a page, and after alignment, ends having a size of 2 pages. // range crosses a page, and after alignment, ends having a size of 2 pages.
if (overlaps.Length == 1 && if (overlapsCount == 1 &&
address >= overlaps[0].Address && address >= overlap0.Address &&
endAddress - overlaps[0].EndAddress <= BufferAlignmentSize * 2) endAddress - overlap0.EndAddress <= BufferAlignmentSize * 2)
{ {
// Try to grow the buffer by 1.5x of its current size. // Try to grow the buffer by 1.5x of its current size.
// This improves performance in the cases where the buffer is resized often by small amounts. // This improves performance in the cases where the buffer is resized often by small amounts.
ulong existingSize = overlaps[0].Value.Size; ulong existingSize = overlap0.Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask; ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize); size = Math.Max(size, growthSize);
endAddress = address + size; endAddress = address + size;
overlaps = _buffers.FindOverlapsAsSpan(address, size); overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
} }
address = Math.Min(address, overlaps[0].Address); for (int index = 0; index < overlapsCount; index++)
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
for (int i = 0; i < overlaps.Length; i++)
{ {
anySparseCompatible |= overlaps[i].Value.SparseCompatible; Buffer buffer = overlaps[index];
anySparseCompatible |= buffer.SparseCompatible;
address = Math.Min(address, buffer.Address);
endAddress = Math.Max(endAddress, buffer.EndAddress);
lock (_buffers)
{
_buffers.Remove(buffer);
}
} }
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
_buffers.Lock.ExitWriteLock();
ulong newSize = endAddress - address; ulong newSize = endAddress - address;
newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray); CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlaps, overlapsCount);
}
else
{
_buffers.Lock.ExitWriteLock();
} }
} }
else else
{ {
_buffers.Lock.ExitWriteLock();
// No overlap, just create a new buffer. // No overlap, just create a new buffer.
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []); Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false);
}
if (newBuffer is not null) lock (_buffers)
{ {
_buffers.Lock.EnterWriteLock(); _buffers.Add(buffer);
_buffers.Add(newBuffer);
_buffers.Lock.ExitWriteLock();
} }
} }
ShrinkOverlapsBufferIfNeeded();
}
/// <summary> /// <summary>
/// Creates a new buffer for the specified range, if needed. /// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists, /// If a buffer where this range can be fully contained already exists,
@@ -582,77 +582,74 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="alignment">Alignment of the start address of the buffer</param> /// <param name="alignment">Alignment of the start address of the buffer</param>
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment) private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment)
{ {
Buffer[] overlaps = _bufferOverlaps;
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
bool sparseAligned = alignment >= SparseBufferAlignmentSize; bool sparseAligned = alignment >= SparseBufferAlignmentSize;
Buffer newBuffer = null;
_buffers.Lock.EnterWriteLock(); if (overlapsCount != 0)
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
if (overlaps.Length != 0)
{ {
// If the buffer already exists, make sure if covers the entire range, // If the buffer already exists, make sure if covers the entire range,
// and make sure it is properly aligned, otherwise sparse mapping may fail. // and make sure it is properly aligned, otherwise sparse mapping may fail.
ulong endAddress = address + size; ulong endAddress = address + size;
Buffer overlap0 = overlaps[0];
if (overlaps[0].Address > address || if (overlap0.Address > address ||
overlaps[0].EndAddress < endAddress || overlap0.EndAddress < endAddress ||
(overlaps[0].Address & (alignment - 1)) != 0 || (overlap0.Address & (alignment - 1)) != 0 ||
(!overlaps[0].Value.SparseCompatible && sparseAligned)) (!overlap0.SparseCompatible && sparseAligned))
{ {
// We need to make sure the new buffer is properly aligned. // We need to make sure the new buffer is properly aligned.
// However, after the range is aligned, it is possible that it // However, after the range is aligned, it is possible that it
// overlaps more buffers, so try again after each extension // overlaps more buffers, so try again after each extension
// and ensure we cover all overlaps. // and ensure we cover all overlaps.
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress); int oldOverlapsCount;
int oldOverlapCount;
do do
{ {
address = Math.Min(address, overlaps[0].Address); for (int index = 0; index < overlapsCount; index++)
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress); {
Buffer buffer = overlaps[index];
address = Math.Min(address, buffer.Address);
endAddress = Math.Max(endAddress, buffer.EndAddress);
}
address &= ~(alignment - 1); address &= ~(alignment - 1);
oldOverlapCount = overlaps.Length; oldOverlapsCount = overlapsCount;
overlaps = _buffers.FindOverlapsAsSpan(address, endAddress - address); overlapsCount = _buffers.FindOverlapsNonOverlapping(address, endAddress - address, ref overlaps);
}
while (oldOverlapsCount != overlapsCount);
lock (_buffers)
{
for (int index = 0; index < overlapsCount; index++)
{
_buffers.Remove(overlaps[index]);
}
} }
while (oldOverlapCount != overlaps.Length);
ulong newSize = endAddress - address; ulong newSize = endAddress - address;
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray(); CreateBufferAligned(address, newSize, stage, sparseAligned, overlaps, overlapsCount);
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
_buffers.Lock.ExitWriteLock();
newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray);
}
else
{
_buffers.Lock.ExitWriteLock();
} }
} }
else else
{ {
_buffers.Lock.ExitWriteLock();
// No overlap, just create a new buffer. // No overlap, just create a new buffer.
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []); Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseAligned);
}
if (newBuffer is not null) lock (_buffers)
{ {
_buffers.Lock.EnterWriteLock(); _buffers.Add(buffer);
_buffers.Add(newBuffer);
_buffers.Lock.ExitWriteLock();
} }
} }
ShrinkOverlapsBufferIfNeeded();
}
/// <summary> /// <summary>
/// Creates a new buffer for the specified range, if needed. /// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists, /// If a buffer where this range can be fully contained already exists,
@@ -663,13 +660,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param> /// <param name="stage">The type of usage that created the buffer</param>
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param> /// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
/// <param name="overlaps">Buffers overlapping the range</param> /// <param name="overlaps">Buffers overlapping the range</param>
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, RangeItem<Buffer>[] overlaps) /// <param name="overlapsCount">Total of overlaps</param>
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, Buffer[] overlaps, int overlapsCount)
{ {
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps); Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps.Take(overlapsCount));
for (int index = 0; index < overlaps.Length; index++) lock (_buffers)
{ {
Buffer buffer = overlaps[index].Value; _buffers.Add(newBuffer);
}
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = overlaps[index];
int dstOffset = (int)(buffer.Address - newBuffer.Address); int dstOffset = (int)(buffer.Address - newBuffer.Address);
@@ -685,8 +688,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
NotifyBuffersModified?.Invoke(); NotifyBuffersModified?.Invoke();
RecreateMultiRangeBuffers(address, size); RecreateMultiRangeBuffers(address, size);
return newBuffer;
} }
/// <summary> /// <summary>
@@ -717,6 +718,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
/// </summary>
private void ShrinkOverlapsBufferIfNeeded()
{
if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity)
{
Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity);
}
}
/// <summary> /// <summary>
/// Copy a buffer data from a given address to another. /// Copy a buffer data from a given address to another.
/// </summary> /// </summary>
@@ -897,7 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
MemoryRange subRange = range.GetSubRange(i); MemoryRange subRange = range.GetSubRange(i);
Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value; Buffer subBuffer = _buffers.FindFirstOverlap(subRange.Address, subRange.Size);
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size); subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
@@ -945,7 +957,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (size != 0) if (size != 0)
{ {
buffer = _buffers.FindOverlap(address, size).Value; buffer = _buffers.FindFirstOverlap(address, size);
buffer.CopyFromDependantVirtualBuffers(); buffer.CopyFromDependantVirtualBuffers();
buffer.SynchronizeMemory(address, size); buffer.SynchronizeMemory(address, size);
@@ -957,7 +969,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
else else
{ {
buffer = _buffers.FindOverlapFast(address, 1).Value; buffer = _buffers.FindFirstOverlap(address, 1);
} }
return buffer; return buffer;
@@ -995,7 +1007,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (size != 0) if (size != 0)
{ {
Buffer buffer = _buffers.FindOverlap(address, size).Value; Buffer buffer = _buffers.FindFirstOverlap(address, size);
if (copyBackVirtual) if (copyBackVirtual)
{ {

View File

@@ -258,7 +258,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa); RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags)); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
@@ -282,7 +282,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa); RecordStorageAlignment(buffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags)); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
@@ -761,7 +761,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!bounds.IsUnmapped) if (!bounds.IsUnmapped)
{ {
bool isWrite = (bounds.Flags & BufferUsageFlags.Write) == BufferUsageFlags.Write; bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite) ? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, bufferStage); : bufferCache.GetBufferRange(bounds.Range, bufferStage);
@@ -798,7 +798,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!bounds.IsUnmapped) if (!bounds.IsUnmapped)
{ {
bool isWrite = (bounds.Flags & BufferUsageFlags.Write) == BufferUsageFlags.Write; bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite) ? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute); : bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
@@ -817,6 +817,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Bind respective buffer bindings on the host API. /// Bind respective buffer bindings on the host API.
/// </summary> /// </summary>
/// <param name="ranges">Host buffers to bind, with their offsets and sizes</param> /// <param name="ranges">Host buffers to bind, with their offsets and sizes</param>
/// <param name="first">First binding point</param>
/// <param name="count">Number of bindings</param> /// <param name="count">Number of bindings</param>
/// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param> /// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -865,6 +866,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="texture">Buffer texture</param> /// <param name="texture">Buffer texture</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param> /// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param> /// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="format">Format of the buffer texture</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param> /// <param name="isImage">Whether the binding is for an image or a sampler</param>
public void SetBufferTextureStorage( public void SetBufferTextureStorage(
ShaderStage stage, ShaderStage stage,
@@ -887,6 +889,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param> /// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param> /// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="index">Index of the binding on the array</param> /// <param name="index">Index of the binding on the array</param>
/// <param name="format">Format of the buffer texture</param>
public void SetBufferTextureStorage( public void SetBufferTextureStorage(
ShaderStage stage, ShaderStage stage,
ITextureArray array, ITextureArray array,
@@ -909,6 +912,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param> /// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param> /// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="index">Index of the binding on the array</param> /// <param name="index">Index of the binding on the array</param>
/// <param name="format">Format of the buffer texture</param>
public void SetBufferTextureStorage( public void SetBufferTextureStorage(
ShaderStage stage, ShaderStage stage,
IImageArray array, IImageArray array,

View File

@@ -1,24 +1,25 @@
using Ryujinx.Common.Pools;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
{ {
/// <summary> /// <summary>
/// A range within a buffer that has been modified by the GPU. /// A range within a buffer that has been modified by the GPU.
/// </summary> /// </summary>
class BufferModifiedRange : INonOverlappingRange class BufferModifiedRange : IRange
{ {
/// <summary> /// <summary>
/// Start address of the range in guest memory. /// Start address of the range in guest memory.
/// </summary> /// </summary>
public ulong Address { get; internal set; } public ulong Address { get; }
/// <summary> /// <summary>
/// Size of the range in bytes. /// Size of the range in bytes.
/// </summary> /// </summary>
public ulong Size { get; internal set; } public ulong Size { get; }
/// <summary> /// <summary>
/// End address of the range in guest memory. /// End address of the range in guest memory.
@@ -60,17 +61,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
return Address < address + size && address < EndAddress; return Address < address + size && address < EndAddress;
} }
public INonOverlappingRange Split(ulong splitAddress)
{
throw new NotImplementedException();
}
} }
/// <summary> /// <summary>
/// A structure used to track GPU modified ranges within a buffer. /// A structure used to track GPU modified ranges within a buffer.
/// </summary> /// </summary>
class BufferModifiedRangeList : NonOverlappingRangeList<BufferModifiedRange> class BufferModifiedRangeList : RangeList<BufferModifiedRange>
{ {
private const int BackingInitialSize = 8; private const int BackingInitialSize = 8;
@@ -81,6 +77,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private BufferMigration _source; private BufferMigration _source;
private BufferModifiedRangeList _migrationTarget; private BufferModifiedRangeList _migrationTarget;
private readonly Lock _lock = new();
/// <summary> /// <summary>
/// Whether the modified range list has any entries or not. /// Whether the modified range list has any entries or not.
/// </summary> /// </summary>
@@ -88,10 +86,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
get get
{ {
Lock.EnterReadLock(); lock (_lock)
bool result = Count > 0; {
Lock.ExitReadLock(); return Count > 0;
return result; }
} }
} }
@@ -116,14 +114,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="action">Action to perform for each remaining sub-range of the input range</param> /// <param name="action">Action to perform for each remaining sub-range of the input range</param>
public void ExcludeModifiedRegions(ulong address, ulong size, Action<ulong, ulong> action) public void ExcludeModifiedRegions(ulong address, ulong size, Action<ulong, ulong> action)
{ {
// Slices a given region using the modified regions in the list. Calls the action for the new slices. lock (_lock)
Lock.EnterReadLock();
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
for (int i = 0; i < overlaps.Length; i++)
{ {
BufferModifiedRange overlap = overlaps[i].Value; // Slices a given region using the modified regions in the list. Calls the action for the new slices.
ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
int count = FindOverlapsNonOverlapping(address, size, ref overlaps);
for (int i = 0; i < count; i++)
{
BufferModifiedRange overlap = overlaps[i];
if (overlap.Address > address) if (overlap.Address > address)
{ {
@@ -136,14 +136,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
address = overlap.EndAddress; address = overlap.EndAddress;
} }
Lock.ExitReadLock();
if ((long)size > 0) if ((long)size > 0)
{ {
// If there is any region left after removing the overlaps, signal it. // If there is any region left after removing the overlaps, signal it.
action(address, size); action(address, size);
} }
} }
}
/// <summary> /// <summary>
/// Signal that a region of the buffer has been modified, and add the new region to the range list. /// Signal that a region of the buffer has been modified, and add the new region to the range list.
@@ -153,93 +152,51 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size of the modified region in bytes</param> /// <param name="size">Size of the modified region in bytes</param>
public void SignalModified(ulong address, ulong size) public void SignalModified(ulong address, ulong size)
{ {
// Must lock, as this can affect flushes from the background thread.
lock (_lock)
{
// We may overlap with some existing modified regions. They must be cut into by the new entry.
ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
int count = FindOverlapsNonOverlapping(address, size, ref overlaps);
ulong endAddress = address + size; ulong endAddress = address + size;
ulong syncNumber = _context.SyncNumber; ulong syncNumber = _context.SyncNumber;
// We may overlap with some existing modified regions. They must be cut into by the new entry.
Lock.EnterWriteLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null) for (int i = 0; i < count; i++)
{ {
Add(new BufferModifiedRange(address, size, syncNumber, this)); // The overlaps must be removed or split.
Lock.ExitWriteLock();
return;
}
if (first == last) BufferModifiedRange overlap = overlaps[i];
{
if (first.Address == address && first.EndAddress == endAddress)
{
first.Value.SyncNumber = syncNumber;
first.Value.Parent = this;
Lock.ExitWriteLock();
return;
}
if (first.Address < address) if (overlap.Address == address && overlap.Size == size)
{ {
first.Value.Size = address - first.Address; // Region already exists. Just update the existing sync number.
Update(first); overlap.SyncNumber = syncNumber;
overlap.Parent = this;
if (first.EndAddress > endAddress)
{
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.Value.SyncNumber, first.Value.Parent));
}
}
else
{
if (first.EndAddress > endAddress)
{
first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress;
Update(first);
}
else
{
Remove(first.Value);
}
}
Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock();
return; return;
} }
BufferModifiedRange buffPre = null; Remove(overlap);
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address) if (overlap.Address < address && overlap.EndAddress > address)
{ {
buffPre = new BufferModifiedRange(first.Address, address - first.Address, // A split item must be created behind this overlap.
first.Value.SyncNumber, first.Value.Parent);
extendsPre = true; Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent));
} }
if (last.EndAddress > endAddress) if (overlap.Address < endAddress && overlap.EndAddress > endAddress)
{ {
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress, // A split item must be created after this overlap.
last.Value.SyncNumber, last.Value.Parent);
extendsPost = true; Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
} }
RemoveRange(first, last);
if (extendsPre)
{
Add(buffPre);
}
if (extendsPost)
{
Add(buffPost);
} }
Add(new BufferModifiedRange(address, size, syncNumber, this)); Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock(); }
} }
/// <summary> /// <summary>
@@ -251,20 +208,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="rangeAction">The action to call for each modified range</param> /// <param name="rangeAction">The action to call for each modified range</param>
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction) public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
{ {
Lock.EnterReadLock(); int count = 0;
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
for (int i = 0; i < overlaps.Length; i++) ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
// Range list must be consistent for this operation.
lock (_lock)
{ {
BufferModifiedRange overlap = overlaps[i].Value; count = FindOverlapsNonOverlapping(address, size, ref overlaps);
}
for (int i = 0; i < count; i++)
{
BufferModifiedRange overlap = overlaps[i];
if (overlap.SyncNumber == syncNumber) if (overlap.SyncNumber == syncNumber)
{ {
rangeAction(overlap.Address, overlap.Size); rangeAction(overlap.Address, overlap.Size);
} }
} }
Lock.ExitReadLock();
} }
/// <summary> /// <summary>
@@ -275,14 +237,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="rangeAction">The action to call for each modified range</param> /// <param name="rangeAction">The action to call for each modified range</param>
public void GetRanges(ulong address, ulong size, Action<ulong, ulong> rangeAction) public void GetRanges(ulong address, ulong size, Action<ulong, ulong> rangeAction)
{ {
// We use the non-span method here because keeping the lock will cause a deadlock. int count = 0;
Lock.EnterReadLock();
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
Lock.ExitReadLock();
for (int i = 0; i < overlaps.Length; i++) ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
// Range list must be consistent for this operation.
lock (_lock)
{ {
BufferModifiedRange overlap = overlaps[i].Value; count = FindOverlapsNonOverlapping(address, size, ref overlaps);
}
for (int i = 0; i < count; i++)
{
BufferModifiedRange overlap = overlaps[i];
rangeAction(overlap.Address, overlap.Size); rangeAction(overlap.Address, overlap.Size);
} }
} }
@@ -295,11 +262,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>True if a range exists in the specified region, false otherwise</returns> /// <returns>True if a range exists in the specified region, false otherwise</returns>
public bool HasRange(ulong address, ulong size) public bool HasRange(ulong address, ulong size)
{ {
Lock.EnterReadLock(); // Range list must be consistent for this operation.
RangeItem<BufferModifiedRange> first = FindOverlapFast(address, size); lock (_lock)
bool result = first is not null; {
Lock.ExitReadLock(); return FindOverlapsNonOverlapping(address, size, ref ThreadStaticArray<BufferModifiedRange>.Get()) > 0;
return result; }
} }
/// <summary> /// <summary>
@@ -331,12 +298,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="address">The start address of the flush range</param> /// <param name="address">The start address of the flush range</param>
/// <param name="endAddress">The end address of the flush range</param> /// <param name="endAddress">The end address of the flush range</param>
private void RemoveRangesAndFlush( private void RemoveRangesAndFlush(
RangeItem<BufferModifiedRange>[] overlaps, BufferModifiedRange[] overlaps,
int rangeCount, int rangeCount,
long highestDiff, long highestDiff,
ulong currentSync, ulong currentSync,
ulong address, ulong address,
ulong endAddress) ulong endAddress)
{
lock (_lock)
{ {
if (_migrationTarget == null) if (_migrationTarget == null)
{ {
@@ -344,7 +313,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++) for (int i = 0; i < rangeCount; i++)
{ {
BufferModifiedRange overlap = overlaps[i].Value; BufferModifiedRange overlap = overlaps[i];
long diff = (long)(overlap.SyncNumber - currentSync); long diff = (long)(overlap.SyncNumber - currentSync);
@@ -361,6 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
return; return;
} }
}
// There is a migration target to call instead. This can't be changed after set so accessing it outside the lock is fine. // There is a migration target to call instead. This can't be changed after set so accessing it outside the lock is fine.
@@ -383,24 +353,31 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong endAddress = address + size; ulong endAddress = address + size;
ulong currentSync = _context.SyncNumber; ulong currentSync = _context.SyncNumber;
int rangeCount = 0;
ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
// Range list must be consistent for this operation // Range list must be consistent for this operation
lock (_lock)
{
if (_migrationTarget != null) if (_migrationTarget != null)
{ {
_migrationTarget!.WaitForAndFlushRanges(address, size); rangeCount = -1;
}
else
{
rangeCount = FindOverlapsNonOverlapping(address, size, ref overlaps);
}
}
if (rangeCount == -1)
{
_migrationTarget.WaitForAndFlushRanges(address, size);
return; return;
} }
else if (rangeCount == 0)
Lock.EnterWriteLock();
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
int rangeCount = overlaps.Length;
if (rangeCount == 0)
{ {
Lock.ExitWriteLock();
return; return;
} }
@@ -411,7 +388,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++) for (int i = 0; i < rangeCount; i++)
{ {
BufferModifiedRange overlap = overlaps[i].Value; BufferModifiedRange overlap = overlaps[i];
long diff = (long)(overlap.SyncNumber - currentSync); long diff = (long)(overlap.SyncNumber - currentSync);
@@ -423,17 +400,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (highestDiff == long.MinValue) if (highestDiff == long.MinValue)
{ {
Lock.ExitWriteLock();
return; return;
} }
// Wait for the syncpoint. // Wait for the syncpoint.
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff); _context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress); RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
Lock.ExitWriteLock();
} }
/// <summary> /// <summary>
@@ -446,10 +419,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="registerRangeAction">The action to call for each modified range</param> /// <param name="registerRangeAction">The action to call for each modified range</param>
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction) public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction)
{ {
ranges.Lock.EnterReadLock(); BufferModifiedRange[] inheritRanges;
BufferModifiedRange[] inheritRanges = ranges.ToArray();
ranges.Lock.ExitReadLock();
lock (ranges._lock)
{
inheritRanges = ranges.ToArray();
lock (_lock)
{
// Copy over the migration from the previous range list // Copy over the migration from the previous range list
BufferMigration oldMigration = ranges._source; BufferMigration oldMigration = ranges._source;
@@ -472,14 +449,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
ranges._migrationTarget = this; ranges._migrationTarget = this;
Lock.EnterWriteLock();
foreach (BufferModifiedRange range in inheritRanges) foreach (BufferModifiedRange range in inheritRanges)
{ {
Add(range); Add(range);
} }
}
Lock.ExitWriteLock(); }
ulong currentSync = _context.SyncNumber; ulong currentSync = _context.SyncNumber;
foreach (BufferModifiedRange range in inheritRanges) foreach (BufferModifiedRange range in inheritRanges)
@@ -498,8 +473,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public void SelfMigration() public void SelfMigration()
{ {
BufferMigrationSpan span = new(_parent, _parent.GetSnapshotDisposeAction(), lock (_lock)
_parent.GetSnapshotFlushAction(), _source); {
BufferMigrationSpan span = new(_parent, _parent.GetSnapshotDisposeAction(), _parent.GetSnapshotFlushAction(), _source);
BufferMigration migration = new([span], this, _context.SyncNumber); BufferMigration migration = new([span], this, _context.SyncNumber);
// Migration target is used to redirect flush actions to the latest range list, // Migration target is used to redirect flush actions to the latest range list,
@@ -507,9 +483,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.RegisterBufferMigration(migration); _context.RegisterBufferMigration(migration);
Lock.EnterWriteLock();
_source = migration; _source = migration;
Lock.ExitWriteLock(); }
} }
/// <summary> /// <summary>
@@ -518,13 +493,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="migration">The migration to remove</param> /// <param name="migration">The migration to remove</param>
public void RemoveMigration(BufferMigration migration) public void RemoveMigration(BufferMigration migration)
{ {
Lock.EnterWriteLock(); lock (_lock)
{
if (_source == migration) if (_source == migration)
{ {
_source = null; _source = null;
} }
}
Lock.ExitWriteLock();
} }
private void ClearPart(BufferModifiedRange overlap, ulong address, ulong endAddress) private void ClearPart(BufferModifiedRange overlap, ulong address, ulong endAddress)
@@ -551,79 +526,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size to clear</param> /// <param name="size">Size to clear</param>
public void Clear(ulong address, ulong size) public void Clear(ulong address, ulong size)
{ {
lock (_lock)
{
// This function can be called from any thread, so it cannot use the arrays for background or foreground.
BufferModifiedRange[] toClear = new BufferModifiedRange[1];
int rangeCount = FindOverlapsNonOverlapping(address, size, ref toClear);
ulong endAddress = address + size; ulong endAddress = address + size;
Lock.EnterWriteLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null) for (int i = 0; i < rangeCount; i++)
{ {
Lock.ExitWriteLock(); BufferModifiedRange overlap = toClear[i];
return;
}
if (first == last) ClearPart(overlap, address, endAddress);
{
if (first.Address < address)
{
first.Value.Size = address - first.Address;
Update(first);
if (first.EndAddress > endAddress)
{
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.Value.SyncNumber, first.Value.Parent));
} }
} }
else
{
if (first.EndAddress > endAddress)
{
first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress;
Update(first);
}
else
{
Remove(first.Value);
}
} }
Lock.ExitWriteLock(); /// <summary>
return; /// Clear all modified ranges.
} /// </summary>
public void Clear()
BufferModifiedRange buffPre = null;
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address)
{ {
buffPre = new BufferModifiedRange(first.Address, address - first.Address, lock (_lock)
first.Value.SyncNumber, first.Value.Parent);
extendsPre = true;
}
if (last.EndAddress > endAddress)
{ {
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress, Count = 0;
last.Value.SyncNumber, last.Value.Parent);
extendsPost = true;
} }
RemoveRange(first, last);
if (extendsPre)
{
Add(buffPre);
}
if (extendsPost)
{
Add(buffPost);
}
Lock.ExitWriteLock();
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@@ -8,7 +7,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Pipeline stages that can modify buffer data, as well as flags indicating storage usage. /// Pipeline stages that can modify buffer data, as well as flags indicating storage usage.
/// Must match ShaderStage for the shader stages, though anything after that can be in any order. /// Must match ShaderStage for the shader stages, though anything after that can be in any order.
/// </summary> /// </summary>
[Flags]
internal enum BufferStage : byte internal enum BufferStage : byte
{ {
Compute, Compute,
@@ -54,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStage FromUsage(BufferUsageFlags flags) public static BufferStage FromUsage(BufferUsageFlags flags)
{ {
if ((flags & BufferUsageFlags.Write) == BufferUsageFlags.Write) if (flags.HasFlag(BufferUsageFlags.Write))
{ {
return BufferStage.StorageWrite; return BufferStage.StorageWrite;
} }
@@ -67,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStage FromUsage(TextureUsageFlags flags) public static BufferStage FromUsage(TextureUsageFlags flags)
{ {
if ((flags & TextureUsageFlags.ImageStore) == TextureUsageFlags.ImageStore) if (flags.HasFlag(TextureUsageFlags.ImageStore))
{ {
return BufferStage.StorageWrite; return BufferStage.StorageWrite;
} }

View File

@@ -691,7 +691,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_pageTable[l0] = new ulong[PtLvl1Size]; _pageTable[l0] = new ulong[PtLvl1Size];
Array.Fill(_pageTable[l0], PteUnmapped); for (ulong index = 0; index < PtLvl1Size; index++)
{
_pageTable[l0][index] = PteUnmapped;
}
} }
_pageTable[l0][l1] = pte; _pageTable[l0][l1] = pte;

View File

@@ -1,6 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -105,11 +104,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="scale">Scale value</param> /// <param name="scale">Scale value</param>
public void UpdateRenderScale(int index, float scale) public void UpdateRenderScale(int index, float scale)
{ {
Span<Vector4<float>> renderScaleSpan = _data.RenderScale.AsSpan(); if (_data.RenderScale[1 + index].X != scale)
if (renderScaleSpan[1 + index].X != scale)
{ {
renderScaleSpan[1 + index].X = scale; _data.RenderScale[1 + index].X = scale;
DirtyRenderScale(1 + index, 1); DirtyRenderScale(1 + index, 1);
} }
} }
@@ -136,13 +133,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="isBgra">True if the format is BGRA< false otherwise</param> /// <param name="isBgra">True if the format is BGRA< false otherwise</param>
public void SetRenderTargetIsBgra(int index, bool isBgra) public void SetRenderTargetIsBgra(int index, bool isBgra)
{ {
Span<Vector4<int>> fragmentIsBgraSpan = _data.FragmentIsBgra.AsSpan(); bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra;
bool isBgraChanged = fragmentIsBgraSpan[index].X != 0 != isBgra;
if (isBgraChanged) if (isBgraChanged)
{ {
fragmentIsBgraSpan[index].X = isBgra ? 1 : 0; _data.FragmentIsBgra[index].X = isBgra ? 1 : 0;
DirtyFragmentIsBgra(index, 1); DirtyFragmentIsBgra(index, 1);
} }
} }

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Represents a GPU virtual memory range. /// Represents a GPU virtual memory range.
/// </summary> /// </summary>
private class VirtualRange : INonOverlappingRange private readonly struct VirtualRange : IRange
{ {
/// <summary> /// <summary>
/// GPU virtual address where the range starts. /// GPU virtual address where the range starts.
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Size of the range in bytes. /// Size of the range in bytes.
/// </summary> /// </summary>
public ulong Size { get; private set; } public ulong Size { get; }
/// <summary> /// <summary>
/// GPU virtual address where the range ends. /// GPU virtual address where the range ends.
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Physical regions where the GPU virtual region is mapped. /// Physical regions where the GPU virtual region is mapped.
/// </summary> /// </summary>
public MultiRange Range { get; private set; } public MultiRange Range { get; }
/// <summary> /// <summary>
/// Creates a new virtual memory range. /// Creates a new virtual memory range.
@@ -60,14 +60,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
return Address < address + size && address < EndAddress; return Address < address + size && address < EndAddress;
} }
public INonOverlappingRange Split(ulong splitAddress)
{
throw new NotImplementedException();
}
} }
private readonly NonOverlappingRangeList<VirtualRange> _virtualRanges; private readonly RangeList<VirtualRange> _virtualRanges;
private VirtualRange[] _virtualRangeOverlaps;
private readonly ConcurrentQueue<VirtualRange> _deferredUnmaps; private readonly ConcurrentQueue<VirtualRange> _deferredUnmaps;
private int _hasDeferredUnmaps; private int _hasDeferredUnmaps;
@@ -79,6 +75,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_memoryManager = memoryManager; _memoryManager = memoryManager;
_virtualRanges = []; _virtualRanges = [];
_virtualRangeOverlaps = new VirtualRange[BufferCache.OverlapsBufferInitialCapacity];
_deferredUnmaps = new ConcurrentQueue<VirtualRange>(); _deferredUnmaps = new ConcurrentQueue<VirtualRange>();
} }
@@ -109,11 +106,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>True if the range already existed, false if a new one was created and added</returns> /// <returns>True if the range already existed, false if a new one was created and added</returns>
public bool TryGetOrAddRange(ulong gpuVa, ulong size, out MultiRange range) public bool TryGetOrAddRange(ulong gpuVa, ulong size, out MultiRange range)
{ {
VirtualRange[] overlaps = _virtualRangeOverlaps;
int overlapsCount;
if (Interlocked.Exchange(ref _hasDeferredUnmaps, 0) != 0) if (Interlocked.Exchange(ref _hasDeferredUnmaps, 0) != 0)
{ {
while (_deferredUnmaps.TryDequeue(out VirtualRange unmappedRange)) while (_deferredUnmaps.TryDequeue(out VirtualRange unmappedRange))
{ {
_virtualRanges.RemoveRange(unmappedRange.Address, unmappedRange.Size); overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(unmappedRange.Address, unmappedRange.Size, ref overlaps);
for (int index = 0; index < overlapsCount; index++)
{
_virtualRanges.Remove(overlaps[index]);
}
} }
} }
@@ -121,22 +126,27 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong originalVa = gpuVa; ulong originalVa = gpuVa;
_virtualRanges.Lock.EnterWriteLock(); overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(gpuVa, size, ref overlaps);
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
if (first is not null) if (overlapsCount != 0)
{ {
// The virtual range already exists. We just need to check if our range fits inside // The virtual range already exists. We just need to check if our range fits inside
// the existing one, and if not, we must extend the existing one. // the existing one, and if not, we must extend the existing one.
ulong endAddress = gpuVa + size; ulong endAddress = gpuVa + size;
VirtualRange overlap0 = overlaps[0];
if (first.Address > gpuVa || first.EndAddress < endAddress) if (overlap0.Address > gpuVa || overlap0.EndAddress < endAddress)
{ {
gpuVa = Math.Min(gpuVa, first.Address); for (int index = 0; index < overlapsCount; index++)
endAddress = Math.Max(endAddress, last.EndAddress); {
VirtualRange virtualRange = overlaps[index];
_virtualRanges.RemoveRange(first, last); gpuVa = Math.Min(gpuVa, virtualRange.Address);
endAddress = Math.Max(endAddress, virtualRange.EndAddress);
_virtualRanges.Remove(virtualRange);
}
ulong newSize = endAddress - gpuVa; ulong newSize = endAddress - gpuVa;
MultiRange newRange = _memoryManager.GetPhysicalRegions(gpuVa, newSize); MultiRange newRange = _memoryManager.GetPhysicalRegions(gpuVa, newSize);
@@ -147,8 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
else else
{ {
found = first.Value.Range.Count == 1 || IsSparseAligned(first.Value.Range); found = overlap0.Range.Count == 1 || IsSparseAligned(overlap0.Range);
range = first.Value.Range.Slice(gpuVa - first.Address, size); range = overlap0.Range.Slice(gpuVa - overlap0.Address, size);
} }
} }
else else
@@ -160,7 +170,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_virtualRanges.Add(virtualRange); _virtualRanges.Add(virtualRange);
} }
_virtualRanges.Lock.ExitWriteLock();
ShrinkOverlapsBufferIfNeeded();
// If the range is not properly aligned for sparse mapping, // If the range is not properly aligned for sparse mapping,
// let's just force it to a single range. // let's just force it to a single range.
@@ -210,5 +221,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return true; return true;
} }
/// <summary>
/// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
/// </summary>
private void ShrinkOverlapsBufferIfNeeded()
{
if (_virtualRangeOverlaps.Length > BufferCache.OverlapsBufferMaxCapacity)
{
Array.Resize(ref _virtualRangeOverlaps, BufferCache.OverlapsBufferMaxCapacity);
}
}
} }
} }

View File

@@ -1,6 +1,5 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
@@ -259,25 +258,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
int count = rtControl.UnpackCount(); int count = rtControl.UnpackCount();
Span<RtColorState> rtColorStateSpan = state.RtColorState.AsSpan();
Span<bool> attachmentEnableSpan = pipeline.AttachmentEnable.AsSpan();
Span<Format> attachmentFormatsSpan = pipeline.AttachmentFormats.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
int rtIndex = rtControl.UnpackPermutationIndex(index); int rtIndex = rtControl.UnpackPermutationIndex(index);
var colorState = rtColorStateSpan[rtIndex]; var colorState = state.RtColorState[rtIndex];
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0) if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
{ {
attachmentEnableSpan[index] = false; pipeline.AttachmentEnable[index] = false;
attachmentFormatsSpan[index] = Format.R8G8B8A8Unorm; pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm;
} }
else else
{ {
attachmentEnableSpan[index] = true; pipeline.AttachmentEnable[index] = true;
attachmentFormatsSpan[index] = colorState.Format.Convert().Format; pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format;
} }
} }
@@ -585,18 +580,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
TransformFeedbackDescriptor[] descs = new TransformFeedbackDescriptor[Constants.TotalTransformFeedbackBuffers]; TransformFeedbackDescriptor[] descs = new TransformFeedbackDescriptor[Constants.TotalTransformFeedbackBuffers];
Span<TfState> tfStateSpan = state.TfState.AsSpan();
Span<Array32<uint>> tfVaryingLocationsSpan = state.TfVaryingLocations.AsSpan();
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++) for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
{ {
var tf = tfStateSpan[i]; var tf = state.TfState[i];
descs[i] = new TransformFeedbackDescriptor( descs[i] = new TransformFeedbackDescriptor(
tf.BufferIndex, tf.BufferIndex,
tf.Stride, tf.Stride,
tf.VaryingsCount, tf.VaryingsCount,
ref tfVaryingLocationsSpan[i]); ref state.TfVaryingLocations[i]);
} }
return descs; return descs;

View File

@@ -580,13 +580,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (ShaderCache.MayConvertVtgToCompute(ref channel.Capabilities) && !vertexAsCompute) if (ShaderCache.MayConvertVtgToCompute(ref channel.Capabilities) && !vertexAsCompute)
{ {
Span<AttributeType> attributeTypesSpan = graphicsState.AttributeTypes.AsSpan(); for (int index = 0; index < graphicsState.AttributeTypes.Length; index++)
Span<AttributeType> gAttributeTypesSpan = GraphicsState.AttributeTypes.AsSpan();
for (int index = 0; index < attributeTypesSpan.Length; index++)
{ {
AttributeType lType = FilterAttributeType(channel, attributeTypesSpan[index]); AttributeType lType = FilterAttributeType(channel, graphicsState.AttributeTypes[index]);
AttributeType rType = FilterAttributeType(channel, gAttributeTypesSpan[index]); AttributeType rType = FilterAttributeType(channel, GraphicsState.AttributeTypes[index]);
if (lType != rType) if (lType != rType)
{ {
@@ -732,8 +729,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
int constantBufferUsePerStageMask = _constantBufferUsePerStage; int constantBufferUsePerStageMask = _constantBufferUsePerStage;
Span<uint> constantBufferUseSpan = ConstantBufferUse.AsSpan();
while (constantBufferUsePerStageMask != 0) while (constantBufferUsePerStageMask != 0)
{ {
int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
@@ -742,7 +737,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
? channel.BufferManager.GetComputeUniformBufferUseMask() ? channel.BufferManager.GetComputeUniformBufferUseMask()
: channel.BufferManager.GetGraphicsUniformBufferUseMask(index); : channel.BufferManager.GetGraphicsUniformBufferUseMask(index);
if (constantBufferUseSpan[index] != useMask) if (ConstantBufferUse[index] != useMask)
{ {
return false; return false;
} }
@@ -806,7 +801,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
if (specializationState != null) if (specializationState != null)
{ {
if ((specializationState.Value.QueriedFlags & QueriedTextureStateFlags.CoordNormalized) == QueriedTextureStateFlags.CoordNormalized && if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) &&
specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized()) specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized())
{ {
return false; return false;
@@ -882,12 +877,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
int constantBufferUsePerStageMask = specState._constantBufferUsePerStage; int constantBufferUsePerStageMask = specState._constantBufferUsePerStage;
Span<uint> constantBufferUseSpan = specState.ConstantBufferUse.AsSpan();
while (constantBufferUsePerStageMask != 0) while (constantBufferUsePerStageMask != 0)
{ {
int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
dataReader.Read(ref constantBufferUseSpan[index]); dataReader.Read(ref specState.ConstantBufferUse[index]);
constantBufferUsePerStageMask &= ~(1 << index); constantBufferUsePerStageMask &= ~(1 << index);
} }
@@ -987,12 +980,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
int constantBufferUsePerStageMask = _constantBufferUsePerStage; int constantBufferUsePerStageMask = _constantBufferUsePerStage;
Span<uint> constantBufferUseSpan = ConstantBufferUse.AsSpan();
while (constantBufferUsePerStageMask != 0) while (constantBufferUsePerStageMask != 0)
{ {
int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
dataWriter.Write(ref constantBufferUseSpan[index]); dataWriter.Write(ref ConstantBufferUse[index]);
constantBufferUsePerStageMask &= ~(1 << index); constantBufferUsePerStageMask &= ~(1 << index);
} }

View File

@@ -92,24 +92,20 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264
if (pictureInfo.ScalingMatrixPresent) if (pictureInfo.ScalingMatrixPresent)
{ {
Span<Array16<byte>> scalingLists4x4Span = pictureInfo.ScalingLists4x4.AsSpan();
for (int index = 0; index < 6; index++) for (int index = 0; index < 6; index++)
{ {
writer.WriteBit(true); writer.WriteBit(true);
WriteScalingList(ref writer, scalingLists4x4Span[index]); WriteScalingList(ref writer, pictureInfo.ScalingLists4x4[index]);
} }
if (pictureInfo.Transform8x8ModeFlag) if (pictureInfo.Transform8x8ModeFlag)
{ {
Span<Array64<byte>> scalingLists8x8Span = pictureInfo.ScalingLists8x8.AsSpan();
for (int index = 0; index < 2; index++) for (int index = 0; index < 2; index++)
{ {
writer.WriteBit(true); writer.WriteBit(true);
WriteScalingList(ref writer, scalingLists8x8Span[index]); WriteScalingList(ref writer, pictureInfo.ScalingLists8x8[index]);
} }
} }
} }
@@ -148,11 +144,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264
int lastScale = 8; int lastScale = 8;
Span<byte> listSpan = list.AsSpan(); for (int index = 0; index < list.Length; index++)
for (int index = 0; index < listSpan.Length; index++)
{ {
byte value = listSpan[scan[index]]; byte value = list[scan[index]];
int deltaScale = value - lastScale; int deltaScale = value - lastScale;

View File

@@ -21,67 +21,49 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private static void ReadTxModeProbs(ref Vp9EntropyProbs txProbs, ref Reader r) private static void ReadTxModeProbs(ref Vp9EntropyProbs txProbs, ref Reader r)
{ {
Span<Array1<byte>> tx8x8ProbSpan1 = txProbs.Tx8x8Prob.AsSpan();
Span<Array2<byte>> tx16x16ProbSpan1 = txProbs.Tx16x16Prob.AsSpan();
Span<Array3<byte>> tx32x32ProbSpan1 = txProbs.Tx32x32Prob.AsSpan();
for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) for (int i = 0; i < EntropyMode.TxSizeContexts; ++i)
{ {
Span<byte> tx8x8ProbSpan2 = tx8x8ProbSpan1[i].AsSpan();
for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j) for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j)
{ {
r.DiffUpdateProb(ref tx8x8ProbSpan2[j]); r.DiffUpdateProb(ref txProbs.Tx8x8Prob[i][j]);
} }
} }
for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) for (int i = 0; i < EntropyMode.TxSizeContexts; ++i)
{ {
Span<byte> tx16x16ProbSpan2 = tx16x16ProbSpan1[i].AsSpan();
for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j) for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j)
{ {
r.DiffUpdateProb(ref tx16x16ProbSpan2[j]); r.DiffUpdateProb(ref txProbs.Tx16x16Prob[i][j]);
} }
} }
for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) for (int i = 0; i < EntropyMode.TxSizeContexts; ++i)
{ {
Span<byte> tx32x32ProbSpan2 = tx32x32ProbSpan1[i].AsSpan();
for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j) for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j)
{ {
r.DiffUpdateProb(ref tx32x32ProbSpan2[j]); r.DiffUpdateProb(ref txProbs.Tx32x32Prob[i][j]);
} }
} }
} }
private static void ReadSwitchableInterpProbs(ref Vp9EntropyProbs fc, ref Reader r) private static void ReadSwitchableInterpProbs(ref Vp9EntropyProbs fc, ref Reader r)
{ {
Span<Array2<byte>> switchableInterpProbSpan1 = fc.SwitchableInterpProb.AsSpan(); for (int j = 0; j < Constants.SwitchableFilterContexts; ++j)
for (int i = 0; i < Constants.SwitchableFilterContexts; ++i)
{ {
Span<byte> switchableInterpProbSpan2 = switchableInterpProbSpan1[i].AsSpan(); for (int i = 0; i < Constants.SwitchableFilters - 1; ++i)
for (int j = 0; j < Constants.SwitchableFilters - 1; ++j)
{ {
r.DiffUpdateProb(ref switchableInterpProbSpan2[j]); r.DiffUpdateProb(ref fc.SwitchableInterpProb[j][i]);
} }
} }
} }
private static void ReadInterModeProbs(ref Vp9EntropyProbs fc, ref Reader r) private static void ReadInterModeProbs(ref Vp9EntropyProbs fc, ref Reader r)
{ {
Span<Array3<byte>> interModeProbSpan1 = fc.InterModeProb.AsSpan();
for (int i = 0; i < Constants.InterModeContexts; ++i) for (int i = 0; i < Constants.InterModeContexts; ++i)
{ {
Span<byte> interModeProbSpan2 = interModeProbSpan1[i].AsSpan();
for (int j = 0; j < Constants.InterModes - 1; ++j) for (int j = 0; j < Constants.InterModes - 1; ++j)
{ {
r.DiffUpdateProb(ref interModeProbSpan2[j]); r.DiffUpdateProb(ref fc.InterModeProb[i][j]);
} }
} }
} }
@@ -90,43 +72,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
r.UpdateMvProbs(ctx.Joints.AsSpan(), EntropyMv.Joints - 1); r.UpdateMvProbs(ctx.Joints.AsSpan(), EntropyMv.Joints - 1);
Span<byte> signSpan = ctx.Sign.AsSpan();
Span<Array10<byte>> classesSpan = ctx.Classes.AsSpan();
Span<Array1<byte>> class0Span = ctx.Class0.AsSpan();
Span<Array10<byte>> bitsSpan = ctx.Bits.AsSpan();
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref signSpan[i], 1), 1); r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Sign[i], 1), 1);
r.UpdateMvProbs(classesSpan[i].AsSpan(), EntropyMv.Classes - 1); r.UpdateMvProbs(ctx.Classes[i].AsSpan(), EntropyMv.Classes - 1);
r.UpdateMvProbs(class0Span[i].AsSpan(), EntropyMv.Class0Size - 1); r.UpdateMvProbs(ctx.Class0[i].AsSpan(), EntropyMv.Class0Size - 1);
r.UpdateMvProbs(bitsSpan[i].AsSpan(), EntropyMv.OffsetBits); r.UpdateMvProbs(ctx.Bits[i].AsSpan(), EntropyMv.OffsetBits);
} }
Span<Array2<Array3<byte>>> class0FpSpan1 = ctx.Class0Fp.AsSpan();
Span<Array3<byte>> fpSpan = ctx.Fp.AsSpan();
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
Span<Array3<byte>> class0FpSpan2 = class0FpSpan1[i].AsSpan();
for (int j = 0; j < EntropyMv.Class0Size; ++j) for (int j = 0; j < EntropyMv.Class0Size; ++j)
{ {
r.UpdateMvProbs(class0FpSpan2[j].AsSpan(), EntropyMv.FpSize - 1); r.UpdateMvProbs(ctx.Class0Fp[i][j].AsSpan(), EntropyMv.FpSize - 1);
} }
r.UpdateMvProbs(fpSpan[i].AsSpan(), 3); r.UpdateMvProbs(ctx.Fp[i].AsSpan(), 3);
} }
if (allowHp) if (allowHp)
{ {
Span<byte> class0HpSpan = ctx.Class0Hp.AsSpan();
Span<byte> hpSpan = ctx.Hp.AsSpan();
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref class0HpSpan[i], 1), 1); r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Class0Hp[i], 1), 1);
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref hpSpan[i], 1), 1); r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Hp[i], 1), 1);
} }
} }
} }
@@ -782,16 +751,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int refr; int refr;
bool isScaled; bool isScaled;
Span<sbyte> refFrameSpan = mi.RefFrame.AsSpan();
Span<Mv> mvSpan = mi.Mv.AsSpan();
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
Span<Ptr<RefBuffer>> blockRefsSpan = xd.BlockRefs.AsSpan();
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
for (refr = 0; refr < 1 + isCompound; ++refr) for (refr = 0; refr < 1 + isCompound; ++refr)
{ {
int frame = refFrameSpan[refr]; int frame = mi.RefFrame[refr];
ref RefBuffer refBuf = ref frameRefsSpan[frame - Constants.LastFrame]; ref RefBuffer refBuf = ref cm.FrameRefs[frame - Constants.LastFrame];
ref ScaleFactors sf = ref refBuf.Sf; ref ScaleFactors sf = ref refBuf.Sf;
ref Surface refFrameBuf = ref refBuf.Buf; ref Surface refFrameBuf = ref refBuf.Buf;
@@ -804,13 +767,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
isScaled = sf.IsScaled(); isScaled = sf.IsScaled();
ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol, ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol,
isScaled ? new Ptr<ScaleFactors>(ref sf) : Ptr<ScaleFactors>.Null); isScaled ? new Ptr<ScaleFactors>(ref sf) : Ptr<ScaleFactors>.Null);
blockRefsSpan[refr] = new Ptr<RefBuffer>(ref refBuf); xd.BlockRefs[refr] = new Ptr<RefBuffer>(ref refBuf);
if (sbType < BlockSize.Block8X8) if (sbType < BlockSize.Block8X8)
{ {
for (plane = 0; plane < Constants.MaxMbPlane; ++plane) for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
{ {
ref MacroBlockDPlane pd = ref planeSpan[plane]; ref MacroBlockDPlane pd = ref xd.Plane[plane];
ref Buf2D dstBuf = ref pd.Dst; ref Buf2D dstBuf = ref pd.Dst;
int num4X4W = pd.N4W; int num4X4W = pd.N4W;
int num4X4H = pd.N4H; int num4X4H = pd.N4H;
@@ -848,10 +811,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
else else
{ {
Mv mv = mvSpan[refr]; Mv mv = mi.Mv[refr];
for (plane = 0; plane < Constants.MaxMbPlane; ++plane) for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
{ {
ref MacroBlockDPlane pd = ref planeSpan[plane]; ref MacroBlockDPlane pd = ref xd.Plane[plane];
ref Buf2D dstBuf = ref pd.Dst; ref Buf2D dstBuf = ref pd.Dst;
int num4X4W = pd.N4W; int num4X4W = pd.N4W;
int num4X4H = pd.N4H; int num4X4H = pd.N4H;
@@ -884,14 +847,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private static void SetPlaneN4(ref MacroBlockD xd, int bw, int bh, int bwl, int bhl) private static void SetPlaneN4(ref MacroBlockD xd, int bw, int bh, int bwl, int bhl)
{ {
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; i++) for (int i = 0; i < Constants.MaxMbPlane; i++)
{ {
planeSpan[i].N4W = (ushort)((bw << 1) >> planeSpan[i].SubsamplingX); xd.Plane[i].N4W = (ushort)((bw << 1) >> xd.Plane[i].SubsamplingX);
planeSpan[i].N4H = (ushort)((bh << 1) >> planeSpan[i].SubsamplingY); xd.Plane[i].N4H = (ushort)((bh << 1) >> xd.Plane[i].SubsamplingY);
planeSpan[i].N4Wl = (byte)(bwl - planeSpan[i].SubsamplingX); xd.Plane[i].N4Wl = (byte)(bwl - xd.Plane[i].SubsamplingX);
planeSpan[i].N4Hl = (byte)(bhl - planeSpan[i].SubsamplingY); xd.Plane[i].N4Hl = (byte)(bhl - xd.Plane[i].SubsamplingY);
} }
} }
@@ -931,7 +892,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// as they are always compared to values that are in 1/8th pel units // as they are always compared to values that are in 1/8th pel units
xd.SetMiRowCol(ref tile, miRow, bh, miCol, bw, cm.MiRows, cm.MiCols); xd.SetMiRowCol(ref tile, miRow, bh, miCol, bw, cm.MiRows, cm.MiCols);
ReconInter.SetupDstPlanes(xd.Plane.AsSpan(), ref xd.CurBuf, miRow, miCol); ReconInter.SetupDstPlanes(ref xd.Plane, ref xd.CurBuf, miRow, miCol);
return ref xd.Mi[0].Value; return ref xd.Mi[0].Value;
} }
@@ -972,11 +933,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (!mi.IsInterBlock()) if (!mi.IsInterBlock())
{ {
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan(); int plane;
for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
for (int plane = 0; plane < Constants.MaxMbPlane; ++plane)
{ {
ref MacroBlockDPlane pd = ref planeSpan[plane]; ref MacroBlockDPlane pd = ref xd.Plane[plane];
TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize;
int num4X4W = pd.N4W; int num4X4W = pd.N4W;
int num4X4H = pd.N4H; int num4X4H = pd.N4H;
@@ -1007,13 +967,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Reconstruction // Reconstruction
if (mi.Skip == 0) if (mi.Skip == 0)
{ {
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
int eobtotal = 0; int eobtotal = 0;
int plane;
for (int plane = 0; plane < Constants.MaxMbPlane; ++plane) for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
{ {
ref MacroBlockDPlane pd = ref planeSpan[plane]; ref MacroBlockDPlane pd = ref xd.Plane[plane];
TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize;
int num4X4W = pd.N4W; int num4X4W = pd.N4W;
int num4X4H = pd.N4H; int num4X4H = pd.N4H;
@@ -1200,30 +1159,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
} }
private static void ReadCoefProbsCommon(ReadOnlySpan<Array2<Array6<Array6<Array3<byte>>>>> coefProbs1, private static void ReadCoefProbsCommon(ref Array2<Array2<Array6<Array6<Array3<byte>>>>> coefProbs,
ref Reader r, int txSize) ref Reader r, int txSize)
{ {
if (r.ReadBit() != 0) if (r.ReadBit() != 0)
{ {
for (int i = 0; i < Constants.PlaneTypes; ++i) for (int i = 0; i < Constants.PlaneTypes; ++i)
{ {
Span<Array6<Array6<Array3<byte>>>> coefProbs2 = coefProbs1[i].AsSpan();
for (int j = 0; j < Entropy.RefTypes; ++j) for (int j = 0; j < Entropy.RefTypes; ++j)
{ {
Span<Array6<Array3<byte>>> coefProbs3 = coefProbs2[j].AsSpan();
for (int k = 0; k < Entropy.CoefBands; ++k) for (int k = 0; k < Entropy.CoefBands; ++k)
{ {
Span<Array3<byte>> coefProbs4 = coefProbs3[k].AsSpan();
for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l) for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l)
{ {
Span<byte> coefProbs5 = coefProbs4[l].AsSpan();
for (int m = 0; m < Entropy.UnconstrainedNodes; ++m) for (int m = 0; m < Entropy.UnconstrainedNodes; ++m)
{ {
r.DiffUpdateProb(ref coefProbs5[m]); r.DiffUpdateProb(ref coefProbs[i][j][k][l][m]);
} }
} }
} }
@@ -1234,13 +1185,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private static void ReadCoefProbs(ref Vp9EntropyProbs fc, TxMode txMode, ref Reader r) private static void ReadCoefProbs(ref Vp9EntropyProbs fc, TxMode txMode, ref Reader r)
{ {
Span<Array2<Array2<Array6<Array6<Array3<byte>>>>>> coefProbsSpan = fc.CoefProbs.AsSpan();
int maxTxSize = (int)Luts.TxModeToBiggestTxSize[(int)txMode]; int maxTxSize = (int)Luts.TxModeToBiggestTxSize[(int)txMode];
for (int txSize = (int)TxSize.Tx4X4; txSize <= maxTxSize; ++txSize) for (int txSize = (int)TxSize.Tx4X4; txSize <= maxTxSize; ++txSize)
{ {
ReadCoefProbsCommon(coefProbsSpan[txSize].AsSpan(), ref r, txSize); ReadCoefProbsCommon(ref fc.CoefProbs[txSize], ref r, txSize);
} }
} }
@@ -1259,14 +1207,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
lf.ModeRefDeltaUpdate = rb.ReadBit() != 0; lf.ModeRefDeltaUpdate = rb.ReadBit() != 0;
if (lf.ModeRefDeltaUpdate) if (lf.ModeRefDeltaUpdate)
{ {
Span<sbyte> refDeltasSpan = lf.RefDeltas.AsSpan();
Span<sbyte> modeDeltasSpan = lf.ModeDeltas.AsSpan();
for (int i = 0; i < LoopFilter.MaxRefLfDeltas; i++) for (int i = 0; i < LoopFilter.MaxRefLfDeltas; i++)
{ {
if (rb.ReadBit() != 0) if (rb.ReadBit() != 0)
{ {
refDeltasSpan[i] = (sbyte)rb.ReadSignedLiteral(6); lf.RefDeltas[i] = (sbyte)rb.ReadSignedLiteral(6);
} }
} }
@@ -1274,7 +1219,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
if (rb.ReadBit() != 0) if (rb.ReadBit() != 0)
{ {
modeDeltasSpan[i] = (sbyte)rb.ReadSignedLiteral(6); lf.ModeDeltas[i] = (sbyte)rb.ReadSignedLiteral(6);
} }
} }
} }
@@ -1322,8 +1267,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.ResizeContextBuffers(allocator, width, height); cm.ResizeContextBuffers(allocator, width, height);
SetupRenderSize(ref cm, ref rb); SetupRenderSize(ref cm, ref rb);
Span<RefCntBuffer> frameBuffsSpan = pool.FrameBufs.AsSpan();
if (cm.GetFrameNewBuffer().ReallocFrameBuffer( if (cm.GetFrameNewBuffer().ReallocFrameBuffer(
allocator, allocator,
cm.Width, cm.Width,
@@ -1333,21 +1276,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.UseHighBitDepth, cm.UseHighBitDepth,
Surface.DecBorderInPixels, Surface.DecBorderInPixels,
cm.ByteAlignment, cm.ByteAlignment,
new Ptr<VpxCodecFrameBuffer>(ref frameBuffsSpan[cm.NewFbIdx].RawFrameBuffer), new Ptr<VpxCodecFrameBuffer>(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer),
FrameBuffers.GetFrameBuffer, FrameBuffers.GetFrameBuffer,
pool.CbPriv) != 0) pool.CbPriv) != 0)
{ {
cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer"); cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer");
} }
frameBuffsSpan[cm.NewFbIdx].Released = 0; pool.FrameBufs[cm.NewFbIdx].Released = 0;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY;
frameBuffsSpan[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight;
} }
private static bool ValidRefFrameImgFmt( private static bool ValidRefFrameImgFmt(
@@ -1368,15 +1311,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
bool hasValidRefFrame = false; bool hasValidRefFrame = false;
ref BufferPool pool = ref cm.BufferPool.Value; ref BufferPool pool = ref cm.BufferPool.Value;
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
if (rb.ReadBit() != 0) if (rb.ReadBit() != 0)
{ {
if (frameRefsSpan[i].Idx != RefBuffer.InvalidIdx) if (cm.FrameRefs[i].Idx != RefBuffer.InvalidIdx)
{ {
ref Surface buf = ref frameRefsSpan[i].Buf; ref Surface buf = ref cm.FrameRefs[i].Buf;
width = buf.YCropWidth; width = buf.YCropWidth;
height = buf.YCropHeight; height = buf.YCropHeight;
found = true; found = true;
@@ -1401,7 +1342,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// has valid dimensions. // has valid dimensions.
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
ref RefBuffer refFrame = ref frameRefsSpan[i]; ref RefBuffer refFrame = ref cm.FrameRefs[i];
hasValidRefFrame |= hasValidRefFrame |=
refFrame.Idx != RefBuffer.InvalidIdx && refFrame.Idx != RefBuffer.InvalidIdx &&
ScaleFactors.ValidRefFrameSize(refFrame.Buf.YCropWidth, refFrame.Buf.YCropHeight, width, ScaleFactors.ValidRefFrameSize(refFrame.Buf.YCropWidth, refFrame.Buf.YCropHeight, width,
@@ -1415,7 +1356,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
ref RefBuffer refFrame = ref frameRefsSpan[i]; ref RefBuffer refFrame = ref cm.FrameRefs[i];
if (refFrame.Idx == RefBuffer.InvalidIdx || if (refFrame.Idx == RefBuffer.InvalidIdx ||
!ValidRefFrameImgFmt( !ValidRefFrameImgFmt(
(BitDepth)refFrame.Buf.BitDepth, (BitDepth)refFrame.Buf.BitDepth,
@@ -1433,8 +1374,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.ResizeContextBuffers(allocator, width, height); cm.ResizeContextBuffers(allocator, width, height);
SetupRenderSize(ref cm, ref rb); SetupRenderSize(ref cm, ref rb);
Span<RefCntBuffer> frameBuffsSpan = pool.FrameBufs.AsSpan();
if (cm.GetFrameNewBuffer().ReallocFrameBuffer( if (cm.GetFrameNewBuffer().ReallocFrameBuffer(
allocator, allocator,
cm.Width, cm.Width,
@@ -1444,21 +1383,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.UseHighBitDepth, cm.UseHighBitDepth,
Surface.DecBorderInPixels, Surface.DecBorderInPixels,
cm.ByteAlignment, cm.ByteAlignment,
new Ptr<VpxCodecFrameBuffer>(ref frameBuffsSpan[cm.NewFbIdx].RawFrameBuffer), new Ptr<VpxCodecFrameBuffer>(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer),
FrameBuffers.GetFrameBuffer, FrameBuffers.GetFrameBuffer,
pool.CbPriv) != 0) pool.CbPriv) != 0)
{ {
cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer"); cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer");
} }
frameBuffsSpan[cm.NewFbIdx].Released = 0; pool.FrameBufs[cm.NewFbIdx].Released = 0;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY;
frameBuffsSpan[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight;
} }
// Reads the next tile returning its size and adjusting '*data' accordingly // Reads the next tile returning its size and adjusting '*data' accordingly
@@ -1498,7 +1437,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
private static void GetTileBuffers(ref Vp9Common cm, ArrayPtr<byte> data, int tileCols, private static void GetTileBuffers(ref Vp9Common cm, ArrayPtr<byte> data, int tileCols,
Span<TileBuffer> tileBuffers) ref Array64<TileBuffer> tileBuffers)
{ {
for (int c = 0; c < tileCols; ++c) for (int c = 0; c < tileCols; ++c)
{ {
@@ -1514,16 +1453,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ArrayPtr<byte> data, ArrayPtr<byte> data,
int tileCols, int tileCols,
int tileRows, int tileRows,
Span<Array64<TileBuffer>> tileBuffers1) ref Array4<Array64<TileBuffer>> tileBuffers)
{ {
for (int r = 0; r < tileRows; ++r) for (int r = 0; r < tileRows; ++r)
{ {
Span<TileBuffer> tileBuffers2 = tileBuffers1[r].AsSpan();
for (int c = 0; c < tileCols; ++c) for (int c = 0; c < tileCols; ++c)
{ {
bool isLast = r == tileRows - 1 && c == tileCols - 1; bool isLast = r == tileRows - 1 && c == tileCols - 1;
ref TileBuffer buf = ref tileBuffers2[c]; ref TileBuffer buf = ref tileBuffers[r][c];
GetTileBuffer(isLast, ref cm.Error, ref data, ref buf); GetTileBuffer(isLast, ref cm.Error, ref data, ref buf);
} }
} }
@@ -1548,19 +1485,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilter.ResetLfm(ref cm); LoopFilter.ResetLfm(ref cm);
Span<Array64<TileBuffer>> tileBuffers1 = tileBuffers.AsSpan(); GetTileBuffers(ref cm, data, tileCols, tileRows, ref tileBuffers);
Span<TileWorkerData> tileWorkerDataSpan = cm.TileWorkerData.AsSpan();
GetTileBuffers(ref cm, data, tileCols, tileRows, tileBuffers1);
// Load all tile information into tile_data. // Load all tile information into tile_data.
for (tileRow = 0; tileRow < tileRows; ++tileRow) for (tileRow = 0; tileRow < tileRows; ++tileRow)
{ {
Span<TileBuffer> tileBuffers2 = tileBuffers1[tileRow].AsSpan();
for (tileCol = 0; tileCol < tileCols; ++tileCol) for (tileCol = 0; tileCol < tileCols; ++tileCol)
{ {
ref TileBuffer buf = ref tileBuffers2[tileCol]; ref TileBuffer buf = ref tileBuffers[tileRow][tileCol];
ref TileWorkerData tileData = ref tileWorkerDataSpan[(tileCols * tileRow) + tileCol]; ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + tileCol];
tileData.Xd = cm.Mb; tileData.Xd = cm.Mb;
tileData.Xd.Corrupted = false; tileData.Xd.Corrupted = false;
tileData.Xd.Counts = cm.Counts; tileData.Xd.Counts = cm.Counts;
@@ -1580,7 +1512,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (tileCol = 0; tileCol < tileCols; ++tileCol) for (tileCol = 0; tileCol < tileCols; ++tileCol)
{ {
int col = tileCol; int col = tileCol;
ref TileWorkerData tileData = ref tileWorkerDataSpan[(tileCols * tileRow) + col]; ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + col];
tile.SetCol(ref cm, col); tile.SetCol(ref cm, col);
tileData.Xd.LeftContext = new Array3<Array16<sbyte>>(); tileData.Xd.LeftContext = new Array3<Array16<sbyte>>();
tileData.Xd.LeftSegContext = new Array8<sbyte>(); tileData.Xd.LeftSegContext = new Array8<sbyte>();
@@ -1599,11 +1531,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
// Get last tile data. // Get last tile data.
return tileWorkerDataSpan[(tileCols * tileRows) - 1].BitReader.FindEnd(); return cm.TileWorkerData[(tileCols * tileRows) - 1].BitReader.FindEnd();
} }
private static bool DecodeTileCol(ref TileWorkerData tileData, ref Vp9Common cm, private static bool DecodeTileCol(ref TileWorkerData tileData, ref Vp9Common cm,
Span<TileBuffer> tileBuffers) ref Array64<TileBuffer> tileBuffers)
{ {
ref TileInfo tile = ref tileData.Xd.Tile; ref TileInfo tile = ref tileData.Xd.Tile;
int finalCol = (1 << cm.Log2TileCols) - 1; int finalCol = (1 << cm.Log2TileCols) - 1;
@@ -1662,11 +1594,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.AboveContext.AsSpan().Clear(); cm.AboveContext.AsSpan().Clear();
cm.AboveSegContext.AsSpan().Clear(); cm.AboveSegContext.AsSpan().Clear();
Span<TileWorkerData> tileWorkerDataSpan = cm.TileWorkerData.AsSpan();
for (n = 0; n < numWorkers; ++n) for (n = 0; n < numWorkers; ++n)
{ {
ref TileWorkerData tileData = ref tileWorkerDataSpan[n + totalTiles]; ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles];
tileData.Xd = cm.Mb; tileData.Xd = cm.Mb;
tileData.Xd.Counts = new Ptr<Vp9BackwardUpdates>(ref tileData.Counts); tileData.Xd.Counts = new Ptr<Vp9BackwardUpdates>(ref tileData.Counts);
@@ -1674,17 +1604,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
Array64<TileBuffer> tileBuffers = new(); Array64<TileBuffer> tileBuffers = new();
Span<TileBuffer> tileBuffersSpan = tileBuffers.AsSpan();
GetTileBuffers(ref cm, data, tileCols, tileBuffersSpan); GetTileBuffers(ref cm, data, tileCols, ref tileBuffers);
tileBuffersSpan[..tileCols].Sort(CompareTileBuffers); tileBuffers.AsSpan()[..tileCols].Sort(CompareTileBuffers);
if (numWorkers == tileCols) if (numWorkers == tileCols)
{ {
TileBuffer largest = tileBuffersSpan[0]; TileBuffer largest = tileBuffers[0];
tileBuffersSpan[1..].CopyTo(tileBuffersSpan[..^1]); Span<TileBuffer> buffers = tileBuffers.AsSpan();
tileBuffersSpan[tileCols - 1] = largest; buffers[1..].CopyTo(buffers[..(tileBuffers.Length - 1)]);
tileBuffers[tileCols - 1] = largest;
} }
else else
{ {
@@ -1695,9 +1625,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// larger tile implies it is more difficult to decode. // larger tile implies it is more difficult to decode.
while (start < end) while (start < end)
{ {
tmp = tileBuffersSpan[start]; tmp = tileBuffers[start];
tileBuffersSpan[start] = tileBuffersSpan[end]; tileBuffers[start] = tileBuffers[end];
tileBuffersSpan[end] = tmp; tileBuffers[end] = tmp;
start += 2; start += 2;
end -= 2; end -= 2;
} }
@@ -1710,7 +1640,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (n = 0; n < numWorkers; ++n) for (n = 0; n < numWorkers; ++n)
{ {
int count = baseVal + ((remain + n) / numWorkers); int count = baseVal + ((remain + n) / numWorkers);
ref TileWorkerData tileData = ref tileWorkerDataSpan[n + totalTiles]; ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles];
tileData.BufStart = bufStart; tileData.BufStart = bufStart;
tileData.BufEnd = bufStart + count - 1; tileData.BufEnd = bufStart + count - 1;
@@ -1724,7 +1654,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
ref TileWorkerData tileData = ref cmPtr.Value.TileWorkerData[n + totalTiles]; ref TileWorkerData tileData = ref cmPtr.Value.TileWorkerData[n + totalTiles];
if (!DecodeTileCol(ref tileData, ref cmPtr.Value, tileBuffers.AsSpan())) if (!DecodeTileCol(ref tileData, ref cmPtr.Value, ref tileBuffers))
{ {
cmPtr.Value.Mb.Corrupted = true; cmPtr.Value.Mb.Corrupted = true;
} }
@@ -1734,14 +1664,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
if (bitReaderEnd.IsNull) if (bitReaderEnd.IsNull)
{ {
ref TileWorkerData tileData = ref tileWorkerDataSpan[n - 1 + totalTiles]; ref TileWorkerData tileData = ref cm.TileWorkerData[n - 1 + totalTiles];
bitReaderEnd = tileData.DataEnd; bitReaderEnd = tileData.DataEnd;
} }
} }
for (n = 0; n < numWorkers; ++n) for (n = 0; n < numWorkers; ++n)
{ {
ref TileWorkerData tileData = ref tileWorkerDataSpan[n + totalTiles]; ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles];
AccumulateFrameCounts(ref cm.Counts.Value, ref tileData.Counts); AccumulateFrameCounts(ref cm.Counts.Value, ref tileData.Counts);
} }
@@ -1775,7 +1705,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
if (cm.FrameType == FrameType.KeyFrame && cm.CurrentVideoFrame > 0) if (cm.FrameType == FrameType.KeyFrame && cm.CurrentVideoFrame > 0)
{ {
Span<RefCntBuffer> frameBuffs = cm.BufferPool.Value.FrameBufs.AsSpan(); ref Array12<RefCntBuffer> frameBufs = ref cm.BufferPool.Value.FrameBufs;
ref BufferPool pool = ref cm.BufferPool.Value; ref BufferPool pool = ref cm.BufferPool.Value;
for (int i = 0; i < Constants.FrameBuffers; ++i) for (int i = 0; i < Constants.FrameBuffers; ++i)
@@ -1785,11 +1715,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
continue; continue;
} }
frameBuffs[i].RefCount = 0; frameBufs[i].RefCount = 0;
if (frameBuffs[i].Released == 0) if (frameBufs[i].Released == 0)
{ {
FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBuffs[i].RawFrameBuffer); FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[i].RawFrameBuffer);
frameBuffs[i].Released = 1; frameBufs[i].Released = 1;
} }
} }
} }
@@ -1812,7 +1742,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
readSyncCode2 == SyncCode2; readSyncCode2 == SyncCode2;
} }
private static void RefCntFb(Span<RefCntBuffer> bufs, ref int idx, int newIdx) private static void RefCntFb(ref Array12<RefCntBuffer> bufs, ref int idx, int newIdx)
{ {
int refIndex = idx; int refIndex = idx;
@@ -1831,15 +1761,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
ref Vp9Common cm = ref pbi.Common; ref Vp9Common cm = ref pbi.Common;
ref BufferPool pool = ref cm.BufferPool.Value; ref BufferPool pool = ref cm.BufferPool.Value;
Span<RefCntBuffer> frameBuffs = pool.FrameBufs.AsSpan(); ref Array12<RefCntBuffer> frameBufs = ref pool.FrameBufs;
int mask, refIndex = 0; int mask, refIndex = 0;
ulong sz; ulong sz;
cm.LastFrameType = cm.FrameType; cm.LastFrameType = cm.FrameType;
cm.LastIntraOnly = cm.IntraOnly; cm.LastIntraOnly = cm.IntraOnly;
Span<int> refFrameSpan = cm.RefFrameMap.AsSpan();
if (rb.ReadLiteral(2) != FrameMarker) if (rb.ReadLiteral(2) != FrameMarker)
{ {
cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame marker"); cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame marker");
@@ -1855,14 +1783,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (cm.ShowExistingFrame != 0) if (cm.ShowExistingFrame != 0)
{ {
// Show an existing frame directly. // Show an existing frame directly.
int frameToShow = refFrameSpan[rb.ReadLiteral(3)]; int frameToShow = cm.RefFrameMap[rb.ReadLiteral(3)];
if (frameToShow < 0 || frameBuffs[frameToShow].RefCount < 1) if (frameToShow < 0 || frameBufs[frameToShow].RefCount < 1)
{ {
cm.Error.InternalError(CodecErr.UnsupBitstream, cm.Error.InternalError(CodecErr.UnsupBitstream,
$"Buffer {frameToShow} does not contain a decoded frame"); $"Buffer {frameToShow} does not contain a decoded frame");
} }
RefCntFb(frameBuffs, ref cm.NewFbIdx, frameToShow); RefCntFb(ref frameBufs, ref cm.NewFbIdx, frameToShow);
pbi.RefreshFrameFlags = 0; pbi.RefreshFrameFlags = 0;
cm.Lf.FilterLevel = 0; cm.Lf.FilterLevel = 0;
cm.ShowFrame = 1; cm.ShowFrame = 1;
@@ -1884,18 +1812,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.ReadBitdepthColorspaceSampling(ref rb); cm.ReadBitdepthColorspaceSampling(ref rb);
pbi.RefreshFrameFlags = (1 << Constants.RefFrames) - 1; pbi.RefreshFrameFlags = (1 << Constants.RefFrames) - 1;
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
frameRefsSpan[i].Idx = RefBuffer.InvalidIdx; cm.FrameRefs[i].Idx = RefBuffer.InvalidIdx;
frameRefsSpan[i].Buf = default; cm.FrameRefs[i].Buf = default;
} }
SetupFrameSize(allocator, ref cm, ref rb); SetupFrameSize(allocator, ref cm, ref rb);
if (pbi.NeedResync != 0) if (pbi.NeedResync != 0)
{ {
refFrameSpan.Fill(-1); cm.RefFrameMap.AsSpan().Fill(-1);
FlushAllFbOnKey(ref cm); FlushAllFbOnKey(ref cm);
pbi.NeedResync = 0; pbi.NeedResync = 0;
} }
@@ -1934,25 +1860,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
SetupFrameSize(allocator, ref cm, ref rb); SetupFrameSize(allocator, ref cm, ref rb);
if (pbi.NeedResync != 0) if (pbi.NeedResync != 0)
{ {
refFrameSpan.Fill(-1); cm.RefFrameMap.AsSpan().Fill(-1);
pbi.NeedResync = 0; pbi.NeedResync = 0;
} }
} }
else if (pbi.NeedResync != 1) else if (pbi.NeedResync != 1)
{ {
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
Span<sbyte> refFrameSignBiasSpan = cm.RefFrameSignBias.AsSpan();
/* Skip if need resync */ /* Skip if need resync */
pbi.RefreshFrameFlags = rb.ReadLiteral(Constants.RefFrames); pbi.RefreshFrameFlags = rb.ReadLiteral(Constants.RefFrames);
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
int refr = rb.ReadLiteral(Constants.RefFramesLog2); int refr = rb.ReadLiteral(Constants.RefFramesLog2);
int idx = refFrameSpan[refr]; int idx = cm.RefFrameMap[refr];
ref RefBuffer refFrame = ref frameRefsSpan[i]; ref RefBuffer refFrame = ref cm.FrameRefs[i];
refFrame.Idx = idx; refFrame.Idx = idx;
refFrame.Buf = frameBuffs[idx].Buf; refFrame.Buf = frameBufs[idx].Buf;
refFrameSignBiasSpan[Constants.LastFrame + i] = (sbyte)rb.ReadBit(); cm.RefFrameSignBias[Constants.LastFrame + i] = (sbyte)rb.ReadBit();
} }
SetupFrameSizeWithRefs(allocator, ref cm, ref rb); SetupFrameSizeWithRefs(allocator, ref cm, ref rb);
@@ -1962,7 +1885,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
ref RefBuffer refBuf = ref frameRefsSpan[i]; ref RefBuffer refBuf = ref cm.FrameRefs[i];
refBuf.Sf.SetupScaleFactorsForFrame( refBuf.Sf.SetupScaleFactorsForFrame(
refBuf.Buf.YCropWidth, refBuf.Buf.YCropWidth,
refBuf.Buf.YCropHeight, refBuf.Buf.YCropHeight,
@@ -2003,25 +1926,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// below, forcing the use of context 0 for those frame types. // below, forcing the use of context 0 for those frame types.
cm.FrameContextIdx = (uint)rb.ReadLiteral(Constants.FrameContextsLog2); cm.FrameContextIdx = (uint)rb.ReadLiteral(Constants.FrameContextsLog2);
Span<int> nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan();
// Generate next_ref_frame_map. // Generate next_ref_frame_map.
for (mask = pbi.RefreshFrameFlags; mask != 0; mask >>= 1) for (mask = pbi.RefreshFrameFlags; mask != 0; mask >>= 1)
{ {
if ((mask & 1) != 0) if ((mask & 1) != 0)
{ {
nextRefFrameMapSpan[refIndex] = cm.NewFbIdx; cm.NextRefFrameMap[refIndex] = cm.NewFbIdx;
++frameBuffs[cm.NewFbIdx].RefCount; ++frameBufs[cm.NewFbIdx].RefCount;
} }
else else
{ {
nextRefFrameMapSpan[refIndex] = cm.RefFrameMap[refIndex]; cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex];
} }
// Current thread holds the reference frame. // Current thread holds the reference frame.
if (cm.RefFrameMap[refIndex] >= 0) if (cm.RefFrameMap[refIndex] >= 0)
{ {
++frameBuffs[cm.RefFrameMap[refIndex]].RefCount; ++frameBufs[cm.RefFrameMap[refIndex]].RefCount;
} }
++refIndex; ++refIndex;
@@ -2029,11 +1950,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (; refIndex < Constants.RefFrames; ++refIndex) for (; refIndex < Constants.RefFrames; ++refIndex)
{ {
nextRefFrameMapSpan[refIndex] = refFrameSpan[refIndex]; cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex];
// Current thread holds the reference frame. // Current thread holds the reference frame.
if (refFrameSpan[refIndex] >= 0) if (cm.RefFrameMap[refIndex] >= 0)
{ {
++frameBuffs[refFrameSpan[refIndex]].RefCount; ++frameBufs[cm.RefFrameMap[refIndex]].RefCount;
} }
} }
@@ -2080,11 +2001,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ReadCoefProbs(ref fc, cm.TxMode, ref r); ReadCoefProbs(ref fc, cm.TxMode, ref r);
Span<byte> skipProbSpan = fc.SkipProb.AsSpan();
for (int k = 0; k < Constants.SkipContexts; ++k) for (int k = 0; k < Constants.SkipContexts; ++k)
{ {
r.DiffUpdateProb(ref skipProbSpan[k]); r.DiffUpdateProb(ref fc.SkipProb[k]);
} }
if (!cm.FrameIsIntraOnly()) if (!cm.FrameIsIntraOnly())
@@ -2096,11 +2015,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ReadSwitchableInterpProbs(ref fc, ref r); ReadSwitchableInterpProbs(ref fc, ref r);
} }
Span<byte> intraInterProbSpan = fc.IntraInterProb.AsSpan();
for (int i = 0; i < Constants.IntraInterContexts; i++) for (int i = 0; i < Constants.IntraInterContexts; i++)
{ {
r.DiffUpdateProb(ref intraInterProbSpan[i]); r.DiffUpdateProb(ref fc.IntraInterProb[i]);
} }
cm.ReferenceMode = cm.ReadFrameReferenceMode(ref r); cm.ReferenceMode = cm.ReadFrameReferenceMode(ref r);
@@ -2111,27 +2028,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.ReadFrameReferenceModeProbs(ref r); cm.ReadFrameReferenceModeProbs(ref r);
Span<Array9<byte>> yModeProbSpan1 = fc.YModeProb.AsSpan();
for (int j = 0; j < EntropyMode.BlockSizeGroups; j++) for (int j = 0; j < EntropyMode.BlockSizeGroups; j++)
{ {
Span<byte> yModeProbSpan2 = yModeProbSpan1[j].AsSpan();
for (int i = 0; i < Constants.IntraModes - 1; ++i) for (int i = 0; i < Constants.IntraModes - 1; ++i)
{ {
r.DiffUpdateProb(ref yModeProbSpan2[i]); r.DiffUpdateProb(ref fc.YModeProb[j][i]);
} }
} }
Span<Array3<byte>> partitionProbSpan1 = fc.PartitionProb.AsSpan();
for (int j = 0; j < Constants.PartitionContexts; ++j) for (int j = 0; j < Constants.PartitionContexts; ++j)
{ {
Span<byte> partitionProbSpan2 = partitionProbSpan1[j].AsSpan();
for (int i = 0; i < Constants.PartitionTypes - 1; ++i) for (int i = 0; i < Constants.PartitionTypes - 1; ++i)
{ {
r.DiffUpdateProb(ref partitionProbSpan2[i]); r.DiffUpdateProb(ref fc.PartitionProb[j][i]);
} }
} }
@@ -2189,9 +2098,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
xd.SetupBlockPlanes(cm.SubsamplingX, cm.SubsamplingY); xd.SetupBlockPlanes(cm.SubsamplingX, cm.SubsamplingY);
Span<Vp9EntropyProbs> frameContextsSpan = cm.FrameContexts.AsSpan(); cm.Fc = new Ptr<Vp9EntropyProbs>(ref cm.FrameContexts[(int)cm.FrameContextIdx]);
cm.Fc = new Ptr<Vp9EntropyProbs>(ref frameContextsSpan[(int)cm.FrameContextIdx]);
xd.Corrupted = false; xd.Corrupted = false;
newFb.Corrupted = ReadCompressedHeader(ref pbi, data, firstPartitionSize) ? 1 : 0; newFb.Corrupted = ReadCompressedHeader(ref pbi, data, firstPartitionSize) ? 1 : 0;
@@ -2260,7 +2167,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Non frame parallel update frame context here. // Non frame parallel update frame context here.
if (cm.RefreshFrameContext != 0 && contextUpdated == 0) if (cm.RefreshFrameContext != 0 && contextUpdated == 0)
{ {
frameContextsSpan[(int)cm.FrameContextIdx] = cm.Fc.Value; cm.FrameContexts[(int)cm.FrameContextIdx] = cm.Fc.Value;
} }
} }
} }

View File

@@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
return PredictionMode.NearestMv + mode; return PredictionMode.NearestMv + mode;
} }
private static int ReadSegmentId(ref Reader r, ReadOnlySpan<byte> segTreeProbs) private static int ReadSegmentId(ref Reader r, ref Array7<byte> segTreeProbs)
{ {
return r.ReadTree(Luts.SegmentTree, segTreeProbs); return r.ReadTree(Luts.SegmentTree, segTreeProbs.AsSpan());
} }
private static ReadOnlySpan<byte> GetTxProbs(ref Vp9EntropyProbs fc, TxSize maxTxSize, int ctx) private static ReadOnlySpan<byte> GetTxProbs(ref Vp9EntropyProbs fc, TxSize maxTxSize, int ctx)
@@ -187,7 +187,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
return 0; return 0;
} }
segmentId = ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan()); segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId);
return segmentId; return segmentId;
} }
@@ -223,15 +223,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (seg.TemporalUpdate) if (seg.TemporalUpdate)
{ {
byte predProb = Segmentation.GetPredProbSegId(cm.Fc.Value.SegPredProb.AsSpan(), ref xd); byte predProb = Segmentation.GetPredProbSegId(ref cm.Fc.Value.SegPredProb, ref xd);
mi.SegIdPredicted = (sbyte)r.Read(predProb); mi.SegIdPredicted = (sbyte)r.Read(predProb);
segmentId = mi.SegIdPredicted != 0 segmentId = mi.SegIdPredicted != 0
? predictedSegmentId ? predictedSegmentId
: ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan()); : ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
} }
else else
{ {
segmentId = ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan()); segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
} }
SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId);
@@ -272,12 +272,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits
Span<byte> bitsSpan = fc.Bits[mvcomp].AsSpan();
d = 0; d = 0;
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
{ {
d |= r.Read(bitsSpan[i]) << i; d |= r.Read(fc.Bits[mvcomp][i]) << i;
} }
mag = Constants.Class0Size << ((int)mvClass + 2); mag = Constants.Class0Size << ((int)mvClass + 2);
@@ -345,7 +343,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref MacroBlockD xd, ref MacroBlockD xd,
ref Reader r, ref Reader r,
int segmentId, int segmentId,
Span<sbyte> refFrame) ref Array2<sbyte> refFrame)
{ {
ref Vp9EntropyProbs fc = ref cm.Fc.Value; ref Vp9EntropyProbs fc = ref cm.Fc.Value;
@@ -420,25 +418,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
BlockSize bsize = mi.SbType; BlockSize bsize = mi.SbType;
Span<BModeInfo> bmiSpan = mi.Bmi.AsSpan();
switch (bsize) switch (bsize)
{ {
case BlockSize.Block4X4: case BlockSize.Block4X4:
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
bmiSpan[i].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[i].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
} }
mi.Mode = bmiSpan[3].Mode; mi.Mode = mi.Bmi[3].Mode;
break; break;
case BlockSize.Block4X8: case BlockSize.Block4X8:
bmiSpan[0].Mode = bmiSpan[2].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[0].Mode = mi.Bmi[2].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
bmiSpan[1].Mode = bmiSpan[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[1].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
break; break;
case BlockSize.Block8X4: case BlockSize.Block8X4:
bmiSpan[0].Mode = bmiSpan[1].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[0].Mode = mi.Bmi[1].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
bmiSpan[2].Mode = bmiSpan[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[2].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
break; break;
default: default:
mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, Luts.SizeGroupLookup[(int)bsize]); mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, Luts.SizeGroupLookup[(int)bsize]);
@@ -451,19 +447,29 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// modes in GetPredContextSwitchableInterp() // modes in GetPredContextSwitchableInterp()
mi.InterpFilter = Constants.SwitchableFilters; mi.InterpFilter = Constants.SwitchableFilters;
Span<sbyte> refFrameSpan = mi.RefFrame.AsSpan(); mi.RefFrame[0] = Constants.IntraFrame;
mi.RefFrame[1] = Constants.None;
}
refFrameSpan[0] = Constants.IntraFrame; private static void CopyPair(ref Array2<Mv> dst, ref Array2<Mv> src)
refFrameSpan[1] = Constants.None; {
dst[0] = src[0];
dst[1] = src[1];
}
private static void ZeroPair(ref Array2<Mv> dst)
{
dst[0] = new Mv();
dst[1] = new Mv();
} }
private static bool Assign( private static bool Assign(
ref Vp9Common cm, ref Vp9Common cm,
ref MacroBlockD xd, ref MacroBlockD xd,
PredictionMode mode, PredictionMode mode,
Span<Mv> mv, ref Array2<Mv> mv,
Span<Mv> refMv, ref Array2<Mv> refMv,
Span<Mv> nearNearestMv, ref Array2<Mv> nearNearestMv,
int isCompound, int isCompound,
bool allowHp, bool allowHp,
ref Reader r) ref Reader r)
@@ -485,12 +491,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
case PredictionMode.NearMv: case PredictionMode.NearMv:
case PredictionMode.NearestMv: case PredictionMode.NearestMv:
{ {
nearNearestMv.CopyTo(mv); CopyPair(ref mv, ref nearNearestMv);
break; break;
} }
case PredictionMode.ZeroMv: case PredictionMode.ZeroMv:
{ {
mv.Clear(); ZeroPair(ref mv);
break; break;
} }
default: default:
@@ -553,29 +559,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private static bool IsDiffRefFrameAddEb( private static bool IsDiffRefFrameAddEb(
ref ModeInfo mbmi, ref ModeInfo mbmi,
sbyte refFrame, sbyte refFrame,
Span<sbyte> refSignBias, ref Array4<sbyte> refSignBias,
ref int refmvCount, ref int refmvCount,
Span<Mv> mvRefList, Span<Mv> mvRefList,
bool earlyBreak) bool earlyBreak)
{ {
if (mbmi.IsInterBlock()) if (mbmi.IsInterBlock())
{ {
Span<sbyte> refFrameSpan = mbmi.RefFrame.AsSpan(); if (mbmi.RefFrame[0] != refFrame)
Span<Mv> mvSpan = mbmi.Mv.AsSpan();
if (refFrameSpan[0] != refFrame)
{ {
if (AddRefListEb(mbmi.ScaleMv(0, refFrame, refSignBias), ref refmvCount, mvRefList, if (AddRefListEb(mbmi.ScaleMv(0, refFrame, ref refSignBias), ref refmvCount, mvRefList,
earlyBreak)) earlyBreak))
{ {
return true; return true;
} }
} }
if (mbmi.HasSecondRef() && refFrameSpan[1] != refFrame && if (mbmi.HasSecondRef() && mbmi.RefFrame[1] != refFrame &&
Unsafe.As<Mv, int>(ref mvSpan[1]) != Unsafe.As<Mv, int>(ref mvSpan[0])) Unsafe.As<Mv, int>(ref mbmi.Mv[1]) != Unsafe.As<Mv, int>(ref mbmi.Mv[0]))
{ {
if (AddRefListEb(mbmi.ScaleMv(1, refFrame, refSignBias), ref refmvCount, mvRefList, if (AddRefListEb(mbmi.ScaleMv(1, refFrame, ref refSignBias), ref refmvCount, mvRefList,
earlyBreak)) earlyBreak))
{ {
return true; return true;
@@ -600,7 +603,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int block, int block,
int isSub8X8) int isSub8X8)
{ {
Span<sbyte> refSignBias = cm.RefFrameSignBias.AsSpan(); ref Array4<sbyte> refSignBias = ref cm.RefFrameSignBias;
int i, refmvCount = 0; int i, refmvCount = 0;
bool differentRefFound = false; bool differentRefFound = false;
Ptr<MvRef> prevFrameMvs = cm.UsePrevFrameMvs Ptr<MvRef> prevFrameMvs = cm.UsePrevFrameMvs
@@ -627,9 +630,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value;
differentRefFound = true; differentRefFound = true;
Span<sbyte> refFrameSpan = candidateMi.RefFrame.AsSpan(); if (candidateMi.RefFrame[0] == refFrame)
if (refFrameSpan[0] == refFrame)
{ {
if (AddRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount, if (AddRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount,
mvRefList, earlyBreak)) mvRefList, earlyBreak))
@@ -637,7 +638,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
goto Done; goto Done;
} }
} }
else if (refFrameSpan[1] == refFrame) else if (candidateMi.RefFrame[1] == refFrame)
{ {
if (AddRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount, if (AddRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount,
mvRefList, earlyBreak)) mvRefList, earlyBreak))
@@ -660,19 +661,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value;
differentRefFound = true; differentRefFound = true;
Span<sbyte> refFrameSpan = candidate.RefFrame.AsSpan(); if (candidate.RefFrame[0] == refFrame)
Span<Mv> mvSpan = candidate.Mv.AsSpan();
if (refFrameSpan[0] == refFrame)
{ {
if (AddRefListEb(mvSpan[0], ref refmvCount, mvRefList, earlyBreak)) if (AddRefListEb(candidate.Mv[0], ref refmvCount, mvRefList, earlyBreak))
{ {
goto Done; goto Done;
} }
} }
else if (refFrameSpan[1] == refFrame) else if (candidate.RefFrame[1] == refFrame)
{ {
if (AddRefListEb(mvSpan[1], ref refmvCount, mvRefList, earlyBreak)) if (AddRefListEb(candidate.Mv[1], ref refmvCount, mvRefList, earlyBreak))
{ {
goto Done; goto Done;
} }
@@ -683,19 +681,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Check the last frame's mode and mv info. // Check the last frame's mode and mv info.
if (!prevFrameMvs.IsNull) if (!prevFrameMvs.IsNull)
{ {
Span<sbyte> refFrameSpan = prevFrameMvs.Value.RefFrame.AsSpan(); if (prevFrameMvs.Value.RefFrame[0] == refFrame)
Span<Mv> mvSpan = prevFrameMvs.Value.Mv.AsSpan();
if (refFrameSpan[0] == refFrame)
{ {
if (AddRefListEb(mvSpan[0], ref refmvCount, mvRefList, earlyBreak)) if (AddRefListEb(prevFrameMvs.Value.Mv[0], ref refmvCount, mvRefList, earlyBreak))
{ {
goto Done; goto Done;
} }
} }
else if (refFrameSpan[1] == refFrame) else if (prevFrameMvs.Value.RefFrame[1] == refFrame)
{ {
if (AddRefListEb(mvSpan[1], ref refmvCount, mvRefList, earlyBreak)) if (AddRefListEb(prevFrameMvs.Value.Mv[1], ref refmvCount, mvRefList, earlyBreak))
{ {
goto Done; goto Done;
} }
@@ -715,7 +710,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value;
// If the candidate is Intra we don't want to consider its mv. // If the candidate is Intra we don't want to consider its mv.
if (IsDiffRefFrameAddEb(ref candidate, refFrame, refSignBias, ref refmvCount, mvRefList, if (IsDiffRefFrameAddEb(ref candidate, refFrame, ref refSignBias, ref refmvCount, mvRefList,
earlyBreak)) earlyBreak))
{ {
goto Done; goto Done;
@@ -727,13 +722,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Since we still don't have a candidate we'll try the last frame. // Since we still don't have a candidate we'll try the last frame.
if (!prevFrameMvs.IsNull) if (!prevFrameMvs.IsNull)
{ {
Span<sbyte> refFrameSpan = prevFrameMvs.Value.RefFrame.AsSpan(); if (prevFrameMvs.Value.RefFrame[0] != refFrame && prevFrameMvs.Value.RefFrame[0] > Constants.IntraFrame)
Span<Mv> mvSpan = prevFrameMvs.Value.Mv.AsSpan();
if (refFrameSpan[0] != refFrame && refFrameSpan[0] > Constants.IntraFrame)
{ {
Mv mv = mvSpan[0]; Mv mv = prevFrameMvs.Value.Mv[0];
if (refSignBias[refFrameSpan[0]] != refSignBias[refFrame]) if (refSignBias[prevFrameMvs.Value.RefFrame[0]] != refSignBias[refFrame])
{ {
mv.Row *= -1; mv.Row *= -1;
mv.Col *= -1; mv.Col *= -1;
@@ -745,13 +737,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
} }
if (refFrameSpan[1] > Constants.IntraFrame && if (prevFrameMvs.Value.RefFrame[1] > Constants.IntraFrame &&
refFrameSpan[1] != refFrame && prevFrameMvs.Value.RefFrame[1] != refFrame &&
Unsafe.As<Mv, int>(ref mvSpan[1]) != Unsafe.As<Mv, int>(ref prevFrameMvs.Value.Mv[1]) !=
Unsafe.As<Mv, int>(ref mvSpan[0])) Unsafe.As<Mv, int>(ref prevFrameMvs.Value.Mv[0]))
{ {
Mv mv = mvSpan[1]; Mv mv = prevFrameMvs.Value.Mv[1];
if (refSignBias[refFrameSpan[1]] != refSignBias[refFrame]) if (refSignBias[prevFrameMvs.Value.RefFrame[1]] != refSignBias[refFrame])
{ {
mv.Row *= -1; mv.Row *= -1;
mv.Col *= -1; mv.Col *= -1;
@@ -797,7 +789,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
Span<Mv> mvList = stackalloc Mv[Constants.MaxMvRefCandidates]; Span<Mv> mvList = stackalloc Mv[Constants.MaxMvRefCandidates];
ref ModeInfo mi = ref xd.Mi[0].Value; ref ModeInfo mi = ref xd.Mi[0].Value;
Span<BModeInfo> bmi = mi.Bmi.AsSpan(); ref Array4<BModeInfo> bmi = ref mi.Bmi;
int refmvCount; int refmvCount;
Debug.Assert(Constants.MaxMvRefCandidates == 2); Debug.Assert(Constants.MaxMvRefCandidates == 2);
@@ -892,12 +884,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
BlockSize bsize = mi.SbType; BlockSize bsize = mi.SbType;
bool allowHp = cm.AllowHighPrecisionMv; bool allowHp = cm.AllowHighPrecisionMv;
Array2<Mv> bestRefMvs = new(); Array2<Mv> bestRefMvs = new();
Span<Mv> bestRefMvsSpan = bestRefMvs.AsSpan();
int refr, isCompound; int refr, isCompound;
byte interModeCtx; byte interModeCtx;
Span<Position> mvRefSearch = Luts.MvRefBlocks[(int)bsize]; Span<Position> mvRefSearch = Luts.MvRefBlocks[(int)bsize];
ReadRefFrames(ref cm, ref xd, ref r, mi.SegmentId, mi.RefFrame.AsSpan()); ReadRefFrames(ref cm, ref xd, ref r, mi.SegmentId, ref mi.RefFrame);
isCompound = mi.HasSecondRef() ? 1 : 0; isCompound = mi.HasSecondRef() ? 1 : 0;
interModeCtx = GetModeContext(ref cm, ref xd, mvRefSearch, miRow, miCol); interModeCtx = GetModeContext(ref cm, ref xd, mvRefSearch, miRow, miCol);
@@ -929,17 +920,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (mi.Mode != PredictionMode.ZeroMv) if (mi.Mode != PredictionMode.ZeroMv)
{ {
Span<Mv> tmpMvs = stackalloc Mv[Constants.MaxMvRefCandidates]; Span<Mv> tmpMvs = stackalloc Mv[Constants.MaxMvRefCandidates];
Span<sbyte> refFrameSpan = mi.RefFrame.AsSpan();
for (refr = 0; refr < 1 + isCompound; ++refr) for (refr = 0; refr < 1 + isCompound; ++refr)
{ {
sbyte frame = refFrameSpan[refr]; sbyte frame = mi.RefFrame[refr];
int refmvCount; int refmvCount;
refmvCount = DecFindRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol, refmvCount = DecFindRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol,
-1, 0); -1, 0);
DecFindBestRefs(allowHp, tmpMvs, ref bestRefMvsSpan[refr], refmvCount); DecFindBestRefs(allowHp, tmpMvs, ref bestRefMvs[refr], refmvCount);
} }
} }
} }
@@ -955,12 +945,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int idx, idy; int idx, idy;
PredictionMode bMode = 0; PredictionMode bMode = 0;
Array2<Mv> bestSub8X8 = new(); Array2<Mv> bestSub8X8 = new();
Span<Mv> bestSub8X8Span = bestSub8X8.AsSpan();
Span<BModeInfo> bmiSpan = mi.Bmi.AsSpan();
const uint InvalidMv = 0x80008000; const uint InvalidMv = 0x80008000;
// Initialize the 2nd element as even though it won't be used meaningfully // Initialize the 2nd element as even though it won't be used meaningfully
// if isCompound is false. // if isCompound is false.
Unsafe.As<Mv, uint>(ref bestSub8X8Span[1]) = InvalidMv; Unsafe.As<Mv, uint>(ref bestSub8X8[1]) = InvalidMv;
for (idy = 0; idy < 2; idy += num4X4H) for (idy = 0; idy < 2; idy += num4X4H)
{ {
for (idx = 0; idx < 2; idx += num4X4W) for (idx = 0; idx < 2; idx += num4X4W)
@@ -973,11 +961,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (refr = 0; refr < 1 + isCompound; ++refr) for (refr = 0; refr < 1 + isCompound; ++refr)
{ {
AppendSub8X8ForIdx(ref cm, ref xd, mvRefSearch, bMode, j, refr, miRow, miCol, AppendSub8X8ForIdx(ref cm, ref xd, mvRefSearch, bMode, j, refr, miRow, miCol,
ref bestSub8X8Span[refr]); ref bestSub8X8[refr]);
} }
} }
if (!Assign(ref cm, ref xd, bMode, bmiSpan[j].Mv.AsSpan(), bestRefMvs.AsSpan(), bestSub8X8.AsSpan(), if (!Assign(ref cm, ref xd, bMode, ref mi.Bmi[j].Mv, ref bestRefMvs, ref bestSub8X8,
isCompound, allowHp, ref r)) isCompound, allowHp, ref r))
{ {
xd.Corrupted |= true; xd.Corrupted |= true;
@@ -986,22 +974,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (num4X4H == 2) if (num4X4H == 2)
{ {
bmiSpan[j + 2] = bmiSpan[j]; mi.Bmi[j + 2] = mi.Bmi[j];
} }
if (num4X4W == 2) if (num4X4W == 2)
{ {
bmiSpan[j + 1] = bmiSpan[j]; mi.Bmi[j + 1] = mi.Bmi[j];
} }
} }
} }
mi.Mode = bMode; mi.Mode = bMode;
bmiSpan[3].Mv.AsSpan().CopyTo(mi.Mv.AsSpan());
CopyPair(ref mi.Mv, ref mi.Bmi[3].Mv);
} }
else else
{ {
xd.Corrupted |= !Assign(ref cm, ref xd, mi.Mode, mi.Mv.AsSpan(), bestRefMvs.AsSpan(), bestRefMvs.AsSpan(), xd.Corrupted |= !Assign(ref cm, ref xd, mi.Mode, ref mi.Mv, ref bestRefMvs, ref bestRefMvs,
isCompound, allowHp, ref r); isCompound, allowHp, ref r);
} }
} }
@@ -1093,42 +1082,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int miOffset = (miRow * cm.MiCols) + miCol; int miOffset = (miRow * cm.MiCols) + miCol;
Span<sbyte> refFrameSpan = mi.Value.RefFrame.AsSpan();
mi.Value.SegmentId = (sbyte)ReadIntraSegmentId(ref cm, miOffset, xMis, yMis, ref r); mi.Value.SegmentId = (sbyte)ReadIntraSegmentId(ref cm, miOffset, xMis, yMis, ref r);
mi.Value.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.Value.SegmentId, ref r); mi.Value.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.Value.SegmentId, ref r);
mi.Value.TxSize = ReadTxSize(ref cm, ref xd, true, ref r); mi.Value.TxSize = ReadTxSize(ref cm, ref xd, true, ref r);
refFrameSpan[0] = Constants.IntraFrame; mi.Value.RefFrame[0] = Constants.IntraFrame;
refFrameSpan[1] = Constants.None; mi.Value.RefFrame[1] = Constants.None;
Span<BModeInfo> bmiSpan;
switch (bsize) switch (bsize)
{ {
case BlockSize.Block4X4: case BlockSize.Block4X4:
bmiSpan = mi.Value.Bmi.AsSpan();
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
bmiSpan[i].Mode = mi.Value.Bmi[i].Mode =
ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, i)); ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, i));
} }
mi.Value.Mode = bmiSpan[3].Mode; mi.Value.Mode = mi.Value.Bmi[3].Mode;
break; break;
case BlockSize.Block4X8: case BlockSize.Block4X8:
bmiSpan = mi.Value.Bmi.AsSpan(); mi.Value.Bmi[0].Mode = mi.Value.Bmi[2].Mode =
bmiSpan[0].Mode = bmiSpan[2].Mode =
ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0)); ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0));
bmiSpan[1].Mode = bmiSpan[3].Mode = mi.Value.Mode = mi.Value.Bmi[1].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode =
ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 1)); ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 1));
break; break;
case BlockSize.Block8X4: case BlockSize.Block8X4:
bmiSpan = mi.Value.Bmi.AsSpan(); mi.Value.Bmi[0].Mode = mi.Value.Bmi[1].Mode =
bmiSpan[0].Mode = bmiSpan[1].Mode =
ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0)); ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0));
bmiSpan[2].Mode = bmiSpan[3].Mode = mi.Value.Mode = mi.Value.Bmi[2].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode =
ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 2)); ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 2));
break; break;
default: default:
@@ -1139,6 +1119,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
mi.Value.UvMode = ReadIntraMode(ref r, cm.Fc.Value.KfUvModeProb[(int)mi.Value.Mode].AsSpan()); mi.Value.UvMode = ReadIntraMode(ref r, cm.Fc.Value.KfUvModeProb[(int)mi.Value.Mode].AsSpan());
} }
private static void CopyRefFramePair(ref Array2<sbyte> dst, ref Array2<sbyte> src)
{
dst[0] = src[0];
dst[1] = src[1];
}
public static void ReadModeInfo( public static void ReadModeInfo(
ref TileWorkerData twd, ref TileWorkerData twd,
ref Vp9Common cm, ref Vp9Common cm,
@@ -1165,8 +1151,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (int w = 0; w < xMis; ++w) for (int w = 0; w < xMis; ++w)
{ {
ref MvRef mv = ref frameMvs[w]; ref MvRef mv = ref frameMvs[w];
mi.RefFrame.AsSpan().CopyTo(mv.RefFrame.AsSpan()); CopyRefFramePair(ref mv.RefFrame, ref mi.RefFrame);
mi.Mv.AsSpan().CopyTo(mv.Mv.AsSpan()); CopyPair(ref mv.Mv, ref mi.Mv);
} }
frameMvs = frameMvs.Slice(cm.MiCols); frameMvs = frameMvs.Slice(cm.MiCols);

View File

@@ -89,10 +89,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.Lf.RefDeltas = pictureInfo.RefDeltas; cm.Lf.RefDeltas = pictureInfo.RefDeltas;
cm.Lf.ModeDeltas = pictureInfo.ModeDeltas; cm.Lf.ModeDeltas = pictureInfo.ModeDeltas;
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan(); cm.FrameRefs[0].Buf = (Surface)pictureInfo.LastReference;
frameRefsSpan[0].Buf = (Surface)pictureInfo.LastReference; cm.FrameRefs[1].Buf = (Surface)pictureInfo.GoldenReference;
frameRefsSpan[1].Buf = (Surface)pictureInfo.GoldenReference; cm.FrameRefs[2].Buf = (Surface)pictureInfo.AltReference;
frameRefsSpan[2].Buf = (Surface)pictureInfo.AltReference;
cm.Mb.CurBuf = (Surface)output; cm.Mb.CurBuf = (Surface)output;
cm.Mb.SetupBlockPlanes(1, 1); cm.Mb.SetupBlockPlanes(1, 1);

View File

@@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
PlaneType type, PlaneType type,
Span<int> dqcoeff, Span<int> dqcoeff,
TxSize txSize, TxSize txSize,
ReadOnlySpan<short> dq, ref Array2<short> dq,
int ctx, int ctx,
ReadOnlySpan<short> scan, ReadOnlySpan<short> scan,
ReadOnlySpan<short> nb, ReadOnlySpan<short> nb,
@@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref Vp9EntropyProbs fc = ref xd.Fc.Value; ref Vp9EntropyProbs fc = ref xd.Fc.Value;
int refr = xd.Mi[0].Value.IsInterBlock() ? 1 : 0; int refr = xd.Mi[0].Value.IsInterBlock() ? 1 : 0;
int band, c = 0; int band, c = 0;
ReadOnlySpan<Array6<Array3<byte>>> coefProbs = fc.CoefProbs[(int)txSize][(int)type][refr].AsSpan(); ref Array6<Array6<Array3<byte>>> coefProbs = ref fc.CoefProbs[(int)txSize][(int)type][refr];
Span<byte> tokenCache = stackalloc byte[32 * 32]; Span<byte> tokenCache = stackalloc byte[32 * 32];
ReadOnlySpan<byte> bandTranslate = Luts.GetBandTranslate(txSize); ReadOnlySpan<byte> bandTranslate = Luts.GetBandTranslate(txSize);
int dqShift = txSize == TxSize.Tx32X32 ? 1 : 0; int dqShift = txSize == TxSize.Tx32X32 ? 1 : 0;
@@ -57,25 +57,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
uint range = r.Range; uint range = r.Range;
int count = r.Count; int count = r.Count;
ReadOnlySpan<Array6<Array4<uint>>> coefSpan = counts.Coef[(int)txSize][(int)type][refr].AsSpan();
ReadOnlySpan<Array6<uint>> eobBranchSpan = counts.EobBranch[(int)txSize][(int)type][refr].AsSpan();
while (c < maxEob) while (c < maxEob)
{ {
int val = -1; int val = -1;
band = bandTranslate[0]; band = bandTranslate[0];
bandTranslate = bandTranslate[1..]; bandTranslate = bandTranslate[1..];
ReadOnlySpan<byte> prob = coefProbs[band][ctx].AsSpan(); ref Array3<byte> prob = ref coefProbs[band][ctx];
if (!xd.Counts.IsNull) if (!xd.Counts.IsNull)
{ {
++eobBranchSpan[band][ctx]; ++counts.EobBranch[(int)txSize][(int)type][refr][band][ctx];
} }
if (r.ReadBool(prob[EobContextNode], ref value, ref count, ref range) == 0) if (r.ReadBool(prob[EobContextNode], ref value, ref count, ref range) == 0)
{ {
if (!xd.Counts.IsNull) if (!xd.Counts.IsNull)
{ {
++coefSpan[band][ctx][Constants.EobModelToken]; ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.EobModelToken];
} }
break; break;
@@ -85,7 +82,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
if (!xd.Counts.IsNull) if (!xd.Counts.IsNull)
{ {
++coefSpan[band][ctx][Constants.ZeroToken]; ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.ZeroToken];
} }
dqv = dq[1]; dqv = dq[1];
@@ -102,7 +99,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ctx = GetCoefContext(nb, tokenCache, c); ctx = GetCoefContext(nb, tokenCache, c);
band = bandTranslate[0]; band = bandTranslate[0];
bandTranslate = bandTranslate[1..]; bandTranslate = bandTranslate[1..];
prob = coefProbs[band][ctx].AsSpan(); prob = ref coefProbs[band][ctx];
} }
if (r.ReadBool(prob[OneContextNode], ref value, ref count, ref range) != 0) if (r.ReadBool(prob[OneContextNode], ref value, ref count, ref range) != 0)
@@ -110,7 +107,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ReadOnlySpan<byte> p = Luts.Pareto8Full[prob[Constants.PivotNode] - 1]; ReadOnlySpan<byte> p = Luts.Pareto8Full[prob[Constants.PivotNode] - 1];
if (!xd.Counts.IsNull) if (!xd.Counts.IsNull)
{ {
++coefSpan[band][ctx][Constants.TwoToken]; ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.TwoToken];
} }
if (r.ReadBool(p[0], ref value, ref count, ref range) != 0) if (r.ReadBool(p[0], ref value, ref count, ref range) != 0)
@@ -178,7 +175,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
if (!xd.Counts.IsNull) if (!xd.Counts.IsNull)
{ {
++coefSpan[band][ctx][Constants.OneToken]; ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.OneToken];
} }
tokenCache[scan[c]] = 1; tokenCache[scan[c]] = 1;
@@ -235,7 +232,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref Reader r = ref twd.BitReader; ref Reader r = ref twd.BitReader;
ref MacroBlockD xd = ref twd.Xd; ref MacroBlockD xd = ref twd.Xd;
ref MacroBlockDPlane pd = ref xd.Plane[plane]; ref MacroBlockDPlane pd = ref xd.Plane[plane];
Span<short> dequant = pd.SegDequant[segId].AsSpan(); ref Array2<short> dequant = ref pd.SegDequant[segId];
int eob; int eob;
Span<sbyte> a = pd.AboveContext.AsSpan()[x..]; Span<sbyte> a = pd.AboveContext.AsSpan()[x..];
Span<sbyte> l = pd.LeftContext.AsSpan()[y..]; Span<sbyte> l = pd.LeftContext.AsSpan()[y..];
@@ -253,7 +250,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
GetPlaneType(plane), GetPlaneType(plane),
pd.DqCoeff.AsSpan(), pd.DqCoeff.AsSpan(),
txSize, txSize,
dequant, ref dequant,
ctx, ctx,
sc.Scan, sc.Scan,
sc.Neighbors, sc.Neighbors,
@@ -269,7 +266,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
GetPlaneType(plane), GetPlaneType(plane),
pd.DqCoeff.AsSpan(), pd.DqCoeff.AsSpan(),
txSize, txSize,
dequant, ref dequant,
ctx, ctx,
sc.Scan, sc.Scan,
sc.Neighbors, sc.Neighbors,
@@ -286,7 +283,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
GetPlaneType(plane), GetPlaneType(plane),
pd.DqCoeff.AsSpan(), pd.DqCoeff.AsSpan(),
txSize, txSize,
dequant, ref dequant,
ctx, ctx,
sc.Scan, sc.Scan,
sc.Neighbors, sc.Neighbors,
@@ -306,7 +303,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
GetPlaneType(plane), GetPlaneType(plane),
pd.DqCoeff.AsSpan(), pd.DqCoeff.AsSpan(),
txSize, txSize,
dequant, ref dequant,
ctx, ctx,
sc.Scan, sc.Scan,
sc.Neighbors, sc.Neighbors,

View File

@@ -1,6 +1,5 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Common;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
@@ -130,7 +129,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
byte* srcX = &src[xQ4 >> SubpelBits]; byte* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
byte* srcX = &src[xQ4 >> SubpelBits]; byte* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -276,7 +275,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
{ {
byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -311,7 +310,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
{ {
byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -620,7 +619,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
ushort* srcX = &src[xQ4 >> SubpelBits]; ushort* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -656,7 +655,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
ushort* srcX = &src[xQ4 >> SubpelBits]; ushort* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -693,7 +692,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
{ {
ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {
@@ -729,7 +728,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
{ {
ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0; int sum = 0;
for (int k = 0; k < SubpelTaps; ++k) for (int k = 0; k < SubpelTaps; ++k)
{ {

View File

@@ -304,40 +304,31 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
internal static void TxCountsToBranchCounts32X32(ReadOnlySpan<uint> txCount32X32P, internal static void TxCountsToBranchCounts32X32(ReadOnlySpan<uint> txCount32X32P,
ReadOnlySpan<Array2<uint>> ct32X32P) ref Array3<Array2<uint>> ct32X32P)
{ {
Span<uint> ct32X32PSpan0 = ct32X32P[0].AsSpan(); ct32X32P[0][0] = txCount32X32P[(int)TxSize.Tx4X4];
Span<uint> ct32X32PSpan1 = ct32X32P[1].AsSpan(); ct32X32P[0][1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] +
Span<uint> ct32X32PSpan2 = ct32X32P[2].AsSpan();
ct32X32PSpan0[0] = txCount32X32P[(int)TxSize.Tx4X4];
ct32X32PSpan0[1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] +
txCount32X32P[(int)TxSize.Tx32X32]; txCount32X32P[(int)TxSize.Tx32X32];
ct32X32PSpan1[0] = txCount32X32P[(int)TxSize.Tx8X8]; ct32X32P[1][0] = txCount32X32P[(int)TxSize.Tx8X8];
ct32X32PSpan1[1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32]; ct32X32P[1][1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32];
ct32X32PSpan2[0] = txCount32X32P[(int)TxSize.Tx16X16]; ct32X32P[2][0] = txCount32X32P[(int)TxSize.Tx16X16];
ct32X32PSpan2[1] = txCount32X32P[(int)TxSize.Tx32X32]; ct32X32P[2][1] = txCount32X32P[(int)TxSize.Tx32X32];
} }
internal static void TxCountsToBranchCounts16X16(ReadOnlySpan<uint> txCount16X16P, internal static void TxCountsToBranchCounts16X16(ReadOnlySpan<uint> txCount16X16P,
ReadOnlySpan<Array2<uint>> ct16X16P) ref Array2<Array2<uint>> ct16X16P)
{ {
Span<uint> ct16X16PSpan0 = ct16X16P[0].AsSpan(); ct16X16P[0][0] = txCount16X16P[(int)TxSize.Tx4X4];
Span<uint> ct16X16PSpan1 = ct16X16P[1].AsSpan(); ct16X16P[0][1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16];
ct16X16P[1][0] = txCount16X16P[(int)TxSize.Tx8X8];
ct16X16PSpan0[0] = txCount16X16P[(int)TxSize.Tx4X4]; ct16X16P[1][1] = txCount16X16P[(int)TxSize.Tx16X16];
ct16X16PSpan0[1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16];
ct16X16PSpan1[0] = txCount16X16P[(int)TxSize.Tx8X8];
ct16X16PSpan1[1] = txCount16X16P[(int)TxSize.Tx16X16];
} }
internal static void TxCountsToBranchCounts8X8(ReadOnlySpan<uint> txCount8X8P, internal static void TxCountsToBranchCounts8X8(ReadOnlySpan<uint> txCount8X8P,
ReadOnlySpan<Array2<uint>> ct8X8P) ref Array1<Array2<uint>> ct8X8P)
{ {
Span<uint> ct8X8PSpan = ct8X8P[0].AsSpan(); ct8X8P[0][0] = txCount8X8P[(int)TxSize.Tx4X4];
ct8X8P[0][1] = txCount8X8P[(int)TxSize.Tx8X8];
ct8X8PSpan[0] = txCount8X8P[(int)TxSize.Tx4X4];
ct8X8PSpan[1] = txCount8X8P[(int)TxSize.Tx8X8];
} }
public static unsafe void SetupPastIndependence(ref Vp9Common cm) public static unsafe void SetupPastIndependence(ref Vp9Common cm)

View File

@@ -378,11 +378,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int index = shiftY; int index = shiftY;
Span<byte> lflYSpan = lfm.LflY.AsSpan();
for (int i = 0; i < bh; i++) for (int i = 0; i < bh; i++)
{ {
MemoryMarshal.CreateSpan(ref lflYSpan[index], 64 - index)[..bw].Fill((byte)filterLevel); MemoryMarshal.CreateSpan(ref lfm.LflY[index], 64 - index)[..bw].Fill((byte)filterLevel);
index += 8; index += 8;
} }
@@ -447,29 +445,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
const ushort LeftBorderUv = 0x1111; const ushort LeftBorderUv = 0x1111;
const ushort AboveBorderUv = 0x000f; const ushort AboveBorderUv = 0x000f;
Span<ulong> leftYSpan = lfm.LeftY.AsSpan();
Span<ulong> aboveYSpan = lfm.AboveY.AsSpan();
Span<ushort> leftUvSpan = lfm.LeftUv.AsSpan();
Span<ushort> aboveUvSpan = lfm.AboveUv.AsSpan();
// The largest loopfilter we have is 16x16 so we use the 16x16 mask // The largest loopfilter we have is 16x16 so we use the 16x16 mask
// for 32x32 transforms also. // for 32x32 transforms also.
leftYSpan[(int)TxSize.Tx16X16] |= leftYSpan[(int)TxSize.Tx32X32]; lfm.LeftY[(int)TxSize.Tx16X16] |= lfm.LeftY[(int)TxSize.Tx32X32];
aboveYSpan[(int)TxSize.Tx16X16] |= aboveYSpan[(int)TxSize.Tx32X32]; lfm.AboveY[(int)TxSize.Tx16X16] |= lfm.AboveY[(int)TxSize.Tx32X32];
leftUvSpan[(int)TxSize.Tx16X16] |= leftUvSpan[(int)TxSize.Tx32X32]; lfm.LeftUv[(int)TxSize.Tx16X16] |= lfm.LeftUv[(int)TxSize.Tx32X32];
aboveUvSpan[(int)TxSize.Tx16X16] |= aboveUvSpan[(int)TxSize.Tx32X32]; lfm.AboveUv[(int)TxSize.Tx16X16] |= lfm.AboveUv[(int)TxSize.Tx32X32];
// We do at least 8 tap filter on every 32x32 even if the transform size // We do at least 8 tap filter on every 32x32 even if the transform size
// is 4x4. So if the 4x4 is set on a border pixel add it to the 8x8 and // is 4x4. So if the 4x4 is set on a border pixel add it to the 8x8 and
// remove it from the 4x4. // remove it from the 4x4.
leftYSpan[(int)TxSize.Tx8X8] |= leftYSpan[(int)TxSize.Tx4X4] & LeftBorder; lfm.LeftY[(int)TxSize.Tx8X8] |= lfm.LeftY[(int)TxSize.Tx4X4] & LeftBorder;
leftYSpan[(int)TxSize.Tx4X4] &= ~LeftBorder; lfm.LeftY[(int)TxSize.Tx4X4] &= ~LeftBorder;
aboveYSpan[(int)TxSize.Tx8X8] |= aboveYSpan[(int)TxSize.Tx4X4] & AboveBorder; lfm.AboveY[(int)TxSize.Tx8X8] |= lfm.AboveY[(int)TxSize.Tx4X4] & AboveBorder;
aboveYSpan[(int)TxSize.Tx4X4] &= ~AboveBorder; lfm.AboveY[(int)TxSize.Tx4X4] &= ~AboveBorder;
leftUvSpan[(int)TxSize.Tx8X8] |= (ushort)(leftUvSpan[(int)TxSize.Tx4X4] & LeftBorderUv); lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx4X4] & LeftBorderUv);
leftUvSpan[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv); lfm.LeftUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv);
aboveUvSpan[(int)TxSize.Tx8X8] |= (ushort)(aboveUvSpan[(int)TxSize.Tx4X4] & AboveBorderUv); lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx4X4] & AboveBorderUv);
aboveUvSpan[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv); lfm.AboveUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv);
// We do some special edge handling. // We do some special edge handling.
if (miRow + Constants.MiBlockSize > cm.MiRows) if (miRow + Constants.MiBlockSize > cm.MiRows)
@@ -483,10 +476,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Remove values completely outside our border. // Remove values completely outside our border.
for (int i = 0; i < (int)TxSize.Tx32X32; i++) for (int i = 0; i < (int)TxSize.Tx32X32; i++)
{ {
leftYSpan[i] &= maskY; lfm.LeftY[i] &= maskY;
aboveYSpan[i] &= maskY; lfm.AboveY[i] &= maskY;
leftUvSpan[i] &= maskUv; lfm.LeftUv[i] &= maskUv;
aboveUvSpan[i] &= maskUv; lfm.AboveUv[i] &= maskUv;
} }
lfm.Int4X4Y &= maskY; lfm.Int4X4Y &= maskY;
@@ -496,14 +489,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// apply the shorter one instead. // apply the shorter one instead.
if (rows == 1) if (rows == 1)
{ {
aboveUvSpan[(int)TxSize.Tx8X8] |= aboveUvSpan[(int)TxSize.Tx16X16]; lfm.AboveUv[(int)TxSize.Tx8X8] |= lfm.AboveUv[(int)TxSize.Tx16X16];
aboveUvSpan[(int)TxSize.Tx16X16] = 0; lfm.AboveUv[(int)TxSize.Tx16X16] = 0;
} }
if (rows == 5) if (rows == 5)
{ {
aboveUvSpan[(int)TxSize.Tx8X8] |= (ushort)(aboveUvSpan[(int)TxSize.Tx16X16] & 0xff00); lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx16X16] & 0xff00);
aboveUvSpan[(int)TxSize.Tx16X16] &= (ushort)~(aboveUvSpan[(int)TxSize.Tx16X16] & 0xff00); lfm.AboveUv[(int)TxSize.Tx16X16] &= (ushort)~(lfm.AboveUv[(int)TxSize.Tx16X16] & 0xff00);
} }
} }
@@ -523,10 +516,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Remove the bits outside the image edge. // Remove the bits outside the image edge.
for (int i = 0; i < (int)TxSize.Tx32X32; i++) for (int i = 0; i < (int)TxSize.Tx32X32; i++)
{ {
leftYSpan[i] &= maskY; lfm.LeftY[i] &= maskY;
aboveYSpan[i] &= maskY; lfm.AboveY[i] &= maskY;
leftUvSpan[i] &= maskUv; lfm.LeftUv[i] &= maskUv;
aboveUvSpan[i] &= maskUv; lfm.AboveUv[i] &= maskUv;
} }
lfm.Int4X4Y &= maskY; lfm.Int4X4Y &= maskY;
@@ -536,14 +529,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// apply the shorter one instead. // apply the shorter one instead.
if (columns == 1) if (columns == 1)
{ {
leftUvSpan[(int)TxSize.Tx8X8] |= leftUvSpan[(int)TxSize.Tx16X16]; lfm.LeftUv[(int)TxSize.Tx8X8] |= lfm.LeftUv[(int)TxSize.Tx16X16];
leftUvSpan[(int)TxSize.Tx16X16] = 0; lfm.LeftUv[(int)TxSize.Tx16X16] = 0;
} }
if (columns == 5) if (columns == 5)
{ {
leftUvSpan[(int)TxSize.Tx8X8] |= (ushort)(leftUvSpan[(int)TxSize.Tx16X16] & 0xcccc); lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx16X16] & 0xcccc);
leftUvSpan[(int)TxSize.Tx16X16] &= (ushort)~(leftUvSpan[(int)TxSize.Tx16X16] & 0xcccc); lfm.LeftUv[(int)TxSize.Tx16X16] &= (ushort)~(lfm.LeftUv[(int)TxSize.Tx16X16] & 0xcccc);
} }
} }
@@ -553,28 +546,28 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
for (int i = 0; i < (int)TxSize.Tx32X32; i++) for (int i = 0; i < (int)TxSize.Tx32X32; i++)
{ {
leftYSpan[i] &= 0xfefefefefefefefeUL; lfm.LeftY[i] &= 0xfefefefefefefefeUL;
leftUvSpan[i] &= 0xeeee; lfm.LeftUv[i] &= 0xeeee;
} }
} }
// Assert if we try to apply 2 different loop filters at the same position. // Assert if we try to apply 2 different loop filters at the same position.
Debug.Assert((leftYSpan[(int)TxSize.Tx16X16] & leftYSpan[(int)TxSize.Tx8X8]) == 0); Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((leftYSpan[(int)TxSize.Tx16X16] & leftYSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((leftYSpan[(int)TxSize.Tx8X8] & leftYSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.LeftY[(int)TxSize.Tx8X8] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Y & leftYSpan[(int)TxSize.Tx16X16]) == 0); Debug.Assert((lfm.Int4X4Y & lfm.LeftY[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((leftUvSpan[(int)TxSize.Tx16X16] & leftUvSpan[(int)TxSize.Tx8X8]) == 0); Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((leftUvSpan[(int)TxSize.Tx16X16] & leftUvSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((leftUvSpan[(int)TxSize.Tx8X8] & leftUvSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.LeftUv[(int)TxSize.Tx8X8] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Uv & leftUvSpan[(int)TxSize.Tx16X16]) == 0); Debug.Assert((lfm.Int4X4Uv & lfm.LeftUv[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((aboveYSpan[(int)TxSize.Tx16X16] & aboveYSpan[(int)TxSize.Tx8X8]) == 0); Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((aboveYSpan[(int)TxSize.Tx16X16] & aboveYSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((aboveYSpan[(int)TxSize.Tx8X8] & aboveYSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.AboveY[(int)TxSize.Tx8X8] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Y & aboveYSpan[(int)TxSize.Tx16X16]) == 0); Debug.Assert((lfm.Int4X4Y & lfm.AboveY[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((aboveUvSpan[(int)TxSize.Tx16X16] & aboveUvSpan[(int)TxSize.Tx8X8]) == 0); Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((aboveUvSpan[(int)TxSize.Tx16X16] & aboveUvSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((aboveUvSpan[(int)TxSize.Tx8X8] & aboveUvSpan[(int)TxSize.Tx4X4]) == 0); Debug.Assert((lfm.AboveUv[(int)TxSize.Tx8X8] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Uv & aboveUvSpan[(int)TxSize.Tx16X16]) == 0); Debug.Assert((lfm.Int4X4Uv & lfm.AboveUv[(int)TxSize.Tx16X16]) == 0);
} }
public static unsafe void ResetLfm(ref Vp9Common cm) public static unsafe void ResetLfm(ref Vp9Common cm)
@@ -590,8 +583,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
int lvl; int lvl;
Span<LoopFilterThresh> lFThrSpan = lfi.Lfthr.AsSpan();
// For each possible value for the loop filter fill out limits // For each possible value for the loop filter fill out limits
for (lvl = 0; lvl <= MaxLoopFilter; lvl++) for (lvl = 0; lvl <= MaxLoopFilter; lvl++)
{ {
@@ -611,8 +602,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
blockInsideLimit = 1; blockInsideLimit = 1;
} }
lFThrSpan[lvl].Lim.AsSpan().Fill((byte)blockInsideLimit); lfi.Lfthr[lvl].Lim.AsSpan().Fill((byte)blockInsideLimit);
lFThrSpan[lvl].Mblim.AsSpan().Fill((byte)((2 * (lvl + 2)) + blockInsideLimit)); lfi.Lfthr[lvl].Mblim.AsSpan().Fill((byte)((2 * (lvl + 2)) + blockInsideLimit));
} }
} }
@@ -634,11 +625,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
lf.LastSharpnessLevel = lf.SharpnessLevel; lf.LastSharpnessLevel = lf.SharpnessLevel;
} }
Span<Array4<Array2<byte>>> lvlSpan = lfi.Lvl.AsSpan();
Span<sbyte> refDeltasSpan = lf.RefDeltas.AsSpan();
Span<sbyte> modeDeltasSpan = lf.ModeDeltas.AsSpan();
sbyte intraFrameRefDelta = refDeltasSpan[Constants.IntraFrame];
for (segId = 0; segId < Constants.MaxSegments; segId++) for (segId = 0; segId < Constants.MaxSegments; segId++)
{ {
int lvlSeg = defaultFiltLvl; int lvlSeg = defaultFiltLvl;
@@ -653,24 +639,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
// We could get rid of this if we assume that deltas are set to // We could get rid of this if we assume that deltas are set to
// zero when not in use; encoder always uses deltas // zero when not in use; encoder always uses deltas
MemoryMarshal.Cast<Array2<byte>, byte>(lvlSpan[segId].AsSpan()).Fill((byte)lvlSeg); MemoryMarshal.Cast<Array2<byte>, byte>(lfi.Lvl[segId].AsSpan()).Fill((byte)lvlSeg);
} }
else else
{ {
int refr, mode; int refr, mode;
int intraLvl = lvlSeg + (intraFrameRefDelta * scale); int intraLvl = lvlSeg + (lf.RefDeltas[Constants.IntraFrame] * scale);
lvlSpan[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter); lfi.Lvl[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter);
Span<Array2<byte>> lvlSpan2 = lvlSpan[segId].AsSpan();
for (refr = Constants.LastFrame; refr < Constants.MaxRefFrames; ++refr) for (refr = Constants.LastFrame; refr < Constants.MaxRefFrames; ++refr)
{ {
Span<byte> lvlSpan3 = lvlSpan2[refr].AsSpan();
for (mode = 0; mode < MaxModeLfDeltas; ++mode) for (mode = 0; mode < MaxModeLfDeltas; ++mode)
{ {
int interLvl = lvlSeg + (refDeltasSpan[refr] * scale) + (modeDeltasSpan[mode] * scale); int interLvl = lvlSeg + (lf.RefDeltas[refr] * scale) + (lf.ModeDeltas[mode] * scale);
lvlSpan3[mode] = (byte)Math.Clamp(interLvl, 0, MaxLoopFilter); lfi.Lvl[segId][refr][mode] = (byte)Math.Clamp(interLvl, 0, MaxLoopFilter);
} }
} }
} }
@@ -829,32 +811,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
lfis[1] = lfthr[lfl[lflForward]]; lfis[1] = lfthr[lfl[lflForward]];
ss[1] = ss[0].Slice(8 * pitch); ss[1] = ss[0].Slice(8 * pitch);
Span<byte> mblim0Span = lfis[0].Mblim.AsSpan();
Span<byte> lim0Span = lfis[0].Lim.AsSpan();
Span<byte> hevThr0Span = lfis[0].HevThr.AsSpan();
Span<byte> mblim1Span = lfis[1].Mblim.AsSpan();
Span<byte> lim1Span = lfis[1].Lim.AsSpan();
Span<byte> hevThr1Span = lfis[1].HevThr.AsSpan();
if ((mask16X16 & dualOne) != 0) if ((mask16X16 & dualOne) != 0)
{ {
if ((mask16X16 & dualOne) == dualOne) if ((mask16X16 & dualOne) == dualOne)
{ {
LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, mblim0Span[0], lim0Span[0], LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, lfis[0].Mblim[0], lfis[0].Lim[0],
hevThr0Span[0], bd); lfis[0].HevThr[0], bd);
} }
else else
{ {
if ((mask16X16 & 1) == 0) ref LoopFilterThresh lfi = ref lfis[(mask16X16 & 1) == 0 ? 1 : 0];
{ LoopFilterScalar.HighBdLpfVertical16(ss[(mask16X16 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0],
LoopFilterScalar.HighBdLpfVertical16(ss[1], pitch, mblim1Span[0], lfi.Lim[0], lfi.HevThr[0], bd);
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical16(ss[0], pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
} }
} }
@@ -865,26 +833,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfVertical8Dual( LoopFilterScalar.HighBdLpfVertical8Dual(
ss[0], ss[0],
pitch, pitch,
mblim0Span[0], lfis[0].Mblim[0],
lim0Span[0], lfis[0].Lim[0],
hevThr0Span[0], lfis[0].HevThr[0],
mblim1Span[0], lfis[1].Mblim[0],
lim1Span[0], lfis[1].Lim[0],
hevThr1Span[0], lfis[1].HevThr[0],
bd); bd);
} }
else else
{ {
if ((mask8X8 & 1) == 0) ref LoopFilterThresh lfi = ref lfis[(mask8X8 & 1) == 0 ? 1 : 0];
{ LoopFilterScalar.HighBdLpfVertical8(
LoopFilterScalar.HighBdLpfVertical8(ss[1], pitch, mblim1Span[0], ss[(mask8X8 & 1) == 0 ? 1 : 0],
lim1Span[0], hevThr1Span[0], bd); pitch,
} lfi.Mblim[0],
else lfi.Lim[0],
{ lfi.HevThr[0],
LoopFilterScalar.HighBdLpfVertical8(ss[0], pitch, mblim0Span[0], bd);
lim0Span[0], hevThr0Span[0], bd);
}
} }
} }
@@ -895,26 +861,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfVertical4Dual( LoopFilterScalar.HighBdLpfVertical4Dual(
ss[0], ss[0],
pitch, pitch,
mblim0Span[0], lfis[0].Mblim[0],
lim0Span[0], lfis[0].Lim[0],
hevThr0Span[0], lfis[0].HevThr[0],
mblim1Span[0], lfis[1].Mblim[0],
lim1Span[0], lfis[1].Lim[0],
hevThr1Span[0], lfis[1].HevThr[0],
bd); bd);
} }
else else
{ {
if ((mask4X4 & 1) == 0) ref LoopFilterThresh lfi = ref lfis[(mask4X4 & 1) == 0 ? 1 : 0];
{ LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0],
LoopFilterScalar.HighBdLpfVertical4(ss[1], pitch, mblim1Span[0], lfi.Lim[0], lfi.HevThr[0], bd);
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical4(ss[0], pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
} }
} }
@@ -925,26 +884,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfVertical4Dual( LoopFilterScalar.HighBdLpfVertical4Dual(
ss[0].Slice(4), ss[0].Slice(4),
pitch, pitch,
mblim0Span[0], lfis[0].Mblim[0],
lim0Span[0], lfis[0].Lim[0],
hevThr0Span[0], lfis[0].HevThr[0],
mblim1Span[0], lfis[1].Mblim[0],
lim1Span[0], lfis[1].Lim[0],
hevThr1Span[0], lfis[1].HevThr[0],
bd); bd);
} }
else else
{ {
if ((mask4X4Int & 1) == 0) ref LoopFilterThresh lfi = ref lfis[(mask4X4Int & 1) == 0 ? 1 : 0];
{ LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4Int & 1) == 0 ? 1 : 0].Slice(4), pitch,
LoopFilterScalar.HighBdLpfVertical4(ss[1].Slice(4), pitch, mblim1Span[0], lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd);
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical4(ss[0].Slice(4), pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
} }
} }
} }
@@ -1135,21 +1087,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
LoopFilterThresh lfi = lfthr[lfl[0]]; LoopFilterThresh lfi = lfthr[lfl[0]];
Span<byte> mblimSpan = lfi.Mblim.AsSpan();
Span<byte> limSpan = lfi.Lim.AsSpan();
Span<byte> hevThrSpan = lfi.HevThr.AsSpan();
if ((mask16X16 & 1) != 0) if ((mask16X16 & 1) != 0)
{ {
if ((mask16X16 & 3) == 3) if ((mask16X16 & 3) == 3)
{ {
LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, mblimSpan[0], limSpan[0], LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, lfi.Mblim[0], lfi.Lim[0],
hevThrSpan[0], bd); lfi.HevThr[0], bd);
count = 2; count = 2;
} }
else else
{ {
LoopFilterScalar.HighBdLpfHorizontal16(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], LoopFilterScalar.HighBdLpfHorizontal16(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0],
bd); bd);
} }
} }
@@ -1160,19 +1108,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Next block's thresholds. // Next block's thresholds.
LoopFilterThresh lfin = lfthr[lfl[1]]; LoopFilterThresh lfin = lfthr[lfl[1]];
Span<byte> nMblimSpan = lfin.Mblim.AsSpan();
Span<byte> nLimSpan = lfin.Lim.AsSpan();
Span<byte> nHevThrSpan = lfin.HevThr.AsSpan();
LoopFilterScalar.HighBdLpfHorizontal8Dual( LoopFilterScalar.HighBdLpfHorizontal8Dual(
s, s,
pitch, pitch,
mblimSpan[0], lfi.Mblim[0],
limSpan[0], lfi.Lim[0],
hevThrSpan[0], lfi.HevThr[0],
nMblimSpan[0], lfin.Mblim[0],
nLimSpan[0], lfin.Lim[0],
nHevThrSpan[0], lfin.HevThr[0],
bd); bd);
if ((mask4X4Int & 3) == 3) if ((mask4X4Int & 3) == 3)
@@ -1180,36 +1124,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfHorizontal4Dual( LoopFilterScalar.HighBdLpfHorizontal4Dual(
s.Slice(4 * pitch), s.Slice(4 * pitch),
pitch, pitch,
mblimSpan[0], lfi.Mblim[0],
limSpan[0], lfi.Lim[0],
hevThrSpan[0], lfi.HevThr[0],
nMblimSpan[0], lfin.Mblim[0],
nLimSpan[0], lfin.Lim[0],
nHevThrSpan[0], lfin.HevThr[0],
bd); bd);
} }
else if ((mask4X4Int & 1) != 0) else if ((mask4X4Int & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
limSpan[0], hevThrSpan[0], bd); lfi.Lim[0], lfi.HevThr[0], bd);
} }
else if ((mask4X4Int & 2) != 0) else if ((mask4X4Int & 2) != 0)
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, nMblimSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0],
nLimSpan[0], nHevThrSpan[0], bd); lfin.Lim[0], lfin.HevThr[0], bd);
} }
count = 2; count = 2;
} }
else else
{ {
LoopFilterScalar.HighBdLpfHorizontal8(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], LoopFilterScalar.HighBdLpfHorizontal8(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0],
bd); bd);
if ((mask4X4Int & 1) != 0) if ((mask4X4Int & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
limSpan[0], hevThrSpan[0], bd); lfi.Lim[0], lfi.HevThr[0], bd);
} }
} }
} }
@@ -1220,19 +1164,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Next block's thresholds. // Next block's thresholds.
LoopFilterThresh lfin = lfthr[lfl[1]]; LoopFilterThresh lfin = lfthr[lfl[1]];
Span<byte> nMblimSpan = lfin.Mblim.AsSpan();
Span<byte> nLimSpan = lfin.Lim.AsSpan();
Span<byte> nHevThrSpan = lfin.HevThr.AsSpan();
LoopFilterScalar.HighBdLpfHorizontal4Dual( LoopFilterScalar.HighBdLpfHorizontal4Dual(
s, s,
pitch, pitch,
mblimSpan[0], lfi.Mblim[0],
limSpan[0], lfi.Lim[0],
hevThrSpan[0], lfi.HevThr[0],
nMblimSpan[0], lfin.Mblim[0],
nLimSpan[0], lfin.Lim[0],
nHevThrSpan[0], lfin.HevThr[0],
bd); bd);
if ((mask4X4Int & 3) == 3) if ((mask4X4Int & 3) == 3)
@@ -1240,43 +1180,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfHorizontal4Dual( LoopFilterScalar.HighBdLpfHorizontal4Dual(
s.Slice(4 * pitch), s.Slice(4 * pitch),
pitch, pitch,
mblimSpan[0], lfi.Mblim[0],
limSpan[0], lfi.Lim[0],
hevThrSpan[0], lfi.HevThr[0],
nMblimSpan[0], lfin.Mblim[0],
nLimSpan[0], lfin.Lim[0],
nHevThrSpan[0], lfin.HevThr[0],
bd); bd);
} }
else if ((mask4X4Int & 1) != 0) else if ((mask4X4Int & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
limSpan[0], hevThrSpan[0], bd); lfi.Lim[0], lfi.HevThr[0], bd);
} }
else if ((mask4X4Int & 2) != 0) else if ((mask4X4Int & 2) != 0)
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, nMblimSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0],
nLimSpan[0], nHevThrSpan[0], bd); lfin.Lim[0], lfin.HevThr[0], bd);
} }
count = 2; count = 2;
} }
else else
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0],
bd); bd);
if ((mask4X4Int & 1) != 0) if ((mask4X4Int & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
limSpan[0], hevThrSpan[0], bd); lfi.Lim[0], lfi.HevThr[0], bd);
} }
} }
} }
else else
{ {
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], limSpan[0], LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], lfi.Lim[0],
hevThrSpan[0], bd); lfi.HevThr[0], bd);
} }
} }
@@ -1352,29 +1292,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
LoopFilterThresh lfi = lfthr[lfl[0]]; LoopFilterThresh lfi = lfthr[lfl[0]];
Span<byte> mblimSpan = lfi.Mblim.AsSpan();
Span<byte> limSpan = lfi.Lim.AsSpan();
Span<byte> hevThrSpan = lfi.HevThr.AsSpan();
if ((mask & 1) != 0) if ((mask & 1) != 0)
{ {
if ((mask16X16 & 1) != 0) if ((mask16X16 & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfVertical16(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); LoopFilterScalar.HighBdLpfVertical16(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd);
} }
else if ((mask8X8 & 1) != 0) else if ((mask8X8 & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfVertical8(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); LoopFilterScalar.HighBdLpfVertical8(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd);
} }
else if ((mask4X4 & 1) != 0) else if ((mask4X4 & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfVertical4(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); LoopFilterScalar.HighBdLpfVertical4(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd);
} }
} }
if ((mask4X4Int & 1) != 0) if ((mask4X4Int & 1) != 0)
{ {
LoopFilterScalar.HighBdLpfVertical4(s.Slice(4), pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); LoopFilterScalar.HighBdLpfVertical4(s.Slice(4), pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd);
} }
s = s.Slice(8); s = s.Slice(8);
@@ -1619,10 +1555,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
ref Buf2D dst = ref plane.Dst; ref Buf2D dst = ref plane.Dst;
ArrayPtr<byte> dst0 = dst.Buf; ArrayPtr<byte> dst0 = dst.Buf;
Span<ulong> leftYSpan = lfm.LeftY.AsSpan(); ulong mask16X16 = lfm.LeftY[(int)TxSize.Tx16X16];
ulong mask16X16 = leftYSpan[(int)TxSize.Tx16X16]; ulong mask8X8 = lfm.LeftY[(int)TxSize.Tx8X8];
ulong mask8X8 = leftYSpan[(int)TxSize.Tx8X8]; ulong mask4X4 = lfm.LeftY[(int)TxSize.Tx4X4];
ulong mask4X4 = leftYSpan[(int)TxSize.Tx4X4];
ulong mask4X4Int = lfm.Int4X4Y; ulong mask4X4Int = lfm.Int4X4Y;
Debug.Assert(plane.SubsamplingX == 0 && plane.SubsamplingY == 0); Debug.Assert(plane.SubsamplingX == 0 && plane.SubsamplingY == 0);
@@ -1669,10 +1604,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Horizontal pass // Horizontal pass
dst.Buf = dst0; dst.Buf = dst0;
Span<ulong> aboveYSpan = lfm.AboveY.AsSpan(); mask16X16 = lfm.AboveY[(int)TxSize.Tx16X16];
mask16X16 = aboveYSpan[(int)TxSize.Tx16X16]; mask8X8 = lfm.AboveY[(int)TxSize.Tx8X8];
mask8X8 = aboveYSpan[(int)TxSize.Tx8X8]; mask4X4 = lfm.AboveY[(int)TxSize.Tx4X4];
mask4X4 = aboveYSpan[(int)TxSize.Tx4X4];
mask4X4Int = lfm.Int4X4Y; mask4X4Int = lfm.Int4X4Y;
for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r++) for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r++)
@@ -1735,12 +1669,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ArrayPtr<byte> dst0 = dst.Buf; ArrayPtr<byte> dst0 = dst.Buf;
Span<byte> lflUv = stackalloc byte[16]; Span<byte> lflUv = stackalloc byte[16];
Span<byte> lflY = lfm.LflY.AsSpan();
Span<ushort> leftUvSpan = lfm.LeftUv.AsSpan(); ushort mask16X16 = lfm.LeftUv[(int)TxSize.Tx16X16];
ushort mask16X16 = leftUvSpan[(int)TxSize.Tx16X16]; ushort mask8X8 = lfm.LeftUv[(int)TxSize.Tx8X8];
ushort mask8X8 = leftUvSpan[(int)TxSize.Tx8X8]; ushort mask4X4 = lfm.LeftUv[(int)TxSize.Tx4X4];
ushort mask4X4 = leftUvSpan[(int)TxSize.Tx4X4];
ushort mask4X4Int = lfm.Int4X4Uv; ushort mask4X4Int = lfm.Int4X4Uv;
Debug.Assert(plane.SubsamplingX == 1 && plane.SubsamplingY == 1); Debug.Assert(plane.SubsamplingX == 1 && plane.SubsamplingY == 1);
@@ -1750,8 +1682,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
for (int c = 0; c < Constants.MiBlockSize >> 1; c++) for (int c = 0; c < Constants.MiBlockSize >> 1; c++)
{ {
lflUv[(r << 1) + c] = lflY[(r << 3) + (c << 1)]; lflUv[(r << 1) + c] = lfm.LflY[(r << 3) + (c << 1)];
lflUv[((r + 2) << 1) + c] = lflY[((r + 2) << 3) + (c << 1)]; lflUv[((r + 2) << 1) + c] = lfm.LflY[((r + 2) << 3) + (c << 1)];
} }
if (cm.UseHighBitDepth) if (cm.UseHighBitDepth)
@@ -1793,10 +1725,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Horizontal pass // Horizontal pass
dst.Buf = dst0; dst.Buf = dst0;
Span<ushort> aboveUvSpan = lfm.AboveUv.AsSpan(); mask16X16 = lfm.AboveUv[(int)TxSize.Tx16X16];
mask16X16 = aboveUvSpan[(int)TxSize.Tx16X16]; mask8X8 = lfm.AboveUv[(int)TxSize.Tx8X8];
mask8X8 = aboveUvSpan[(int)TxSize.Tx8X8]; mask4X4 = lfm.AboveUv[(int)TxSize.Tx4X4];
mask4X4 = aboveUvSpan[(int)TxSize.Tx4X4];
mask4X4Int = lfm.Int4X4Uv; mask4X4Int = lfm.Int4X4Uv;
for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += 2) for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += 2)
@@ -1875,17 +1806,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int sbCols = TileInfo.MiColsAlignedToSb(cm.MiCols) >> Constants.MiBlockSizeLog2; int sbCols = TileInfo.MiColsAlignedToSb(cm.MiCols) >> Constants.MiBlockSizeLog2;
LfPath path; LfPath path;
int miRow, miCol; int miRow, miCol;
Span<MacroBlockDPlane> planesSpan = planes.AsSpan();
if (yOnly) if (yOnly)
{ {
path = LfPath.LfPath444; path = LfPath.LfPath444;
} }
else if (planesSpan[1].SubsamplingY == 1 && planesSpan[1].SubsamplingX == 1) else if (planes[1].SubsamplingY == 1 && planes[1].SubsamplingX == 1)
{ {
path = LfPath.LfPath420; path = LfPath.LfPath420;
} }
else if (planesSpan[1].SubsamplingY == 0 && planesSpan[1].SubsamplingX == 0) else if (planes[1].SubsamplingY == 0 && planes[1].SubsamplingX == 0)
{ {
path = LfPath.LfPath444; path = LfPath.LfPath444;
} }
@@ -1907,23 +1837,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
lfSync.SyncRead(r, c); lfSync.SyncRead(r, c);
ReconInter.SetupDstPlanes(planesSpan, ref frameBuffer, miRow, miCol); ReconInter.SetupDstPlanes(ref planes, ref frameBuffer, miRow, miCol);
AdjustMask(ref cm, miRow, miCol, ref lfm[0]); AdjustMask(ref cm, miRow, miCol, ref lfm[0]);
FilterBlockPlaneSs00(ref cm, ref planesSpan[0], miRow, ref lfm[0]); FilterBlockPlaneSs00(ref cm, ref planes[0], miRow, ref lfm[0]);
for (plane = 1; plane < numPlanes; ++plane) for (plane = 1; plane < numPlanes; ++plane)
{ {
switch (path) switch (path)
{ {
case LfPath.LfPath420: case LfPath.LfPath420:
FilterBlockPlaneSs11(ref cm, ref planesSpan[plane], miRow, ref lfm[0]); FilterBlockPlaneSs11(ref cm, ref planes[plane], miRow, ref lfm[0]);
break; break;
case LfPath.LfPath444: case LfPath.LfPath444:
FilterBlockPlaneSs00(ref cm, ref planesSpan[plane], miRow, ref lfm[0]); FilterBlockPlaneSs00(ref cm, ref planes[plane], miRow, ref lfm[0]);
break; break;
case LfPath.LfPathSlow: case LfPath.LfPathSlow:
FilterBlockPlaneNon420(ref cm, ref planesSpan[plane], mi.Slice(miCol), miRow, FilterBlockPlaneNon420(ref cm, ref planes[plane], mi.Slice(miCol), miRow,
miCol); miCol);
break; break;
} }

View File

@@ -270,16 +270,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private static Array8<short> NewArray8Short(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7) private static Array8<short> NewArray8Short(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7)
{ {
Array8<short> output = new(); Array8<short> output = new()
Span<short> outputSpan = output.AsSpan(); {
outputSpan[0] = e0; [0] = e0,
outputSpan[1] = e1; [1] = e1,
outputSpan[2] = e2; [2] = e2,
outputSpan[3] = e3; [3] = e3,
outputSpan[4] = e4; [4] = e4,
outputSpan[5] = e5; [5] = e5,
outputSpan[6] = e6; [6] = e6,
outputSpan[7] = e7; [7] = e7
};
return output; return output;
} }

View File

@@ -1,5 +1,4 @@
using Ryujinx.Graphics.Nvdec.Vp9.Types; using Ryujinx.Graphics.Nvdec.Vp9.Types;
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.Graphics.Nvdec.Vp9 namespace Ryujinx.Graphics.Nvdec.Vp9
@@ -110,17 +109,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
sbyte vrfa = aSg ? xd.AboveMi.Value.RefFrame[0] : xd.AboveMi.Value.RefFrame[varRefIdx]; sbyte vrfa = aSg ? xd.AboveMi.Value.RefFrame[0] : xd.AboveMi.Value.RefFrame[varRefIdx];
sbyte vrfl = lSg ? xd.LeftMi.Value.RefFrame[0] : xd.LeftMi.Value.RefFrame[varRefIdx]; sbyte vrfl = lSg ? xd.LeftMi.Value.RefFrame[0] : xd.LeftMi.Value.RefFrame[varRefIdx];
Span<sbyte> compVarRefSpan = cm.CompVarRef.AsSpan(); if (vrfa == vrfl && cm.CompVarRef[1] == vrfa)
if (vrfa == vrfl && compVarRefSpan[1] == vrfa)
{ {
predContext = 0; predContext = 0;
} }
else if (lSg && aSg) else if (lSg && aSg)
{ {
// Single/Single // Single/Single
if ((vrfa == cm.CompFixedRef && vrfl == compVarRefSpan[0]) || if ((vrfa == cm.CompFixedRef && vrfl == cm.CompVarRef[0]) ||
(vrfl == cm.CompFixedRef && vrfa == compVarRefSpan[0])) (vrfl == cm.CompFixedRef && vrfa == cm.CompVarRef[0]))
{ {
predContext = 4; predContext = 4;
} }
@@ -138,11 +135,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Single/Comp // Single/Comp
sbyte vrfc = lSg ? vrfa : vrfl; sbyte vrfc = lSg ? vrfa : vrfl;
sbyte rfs = aSg ? vrfa : vrfl; sbyte rfs = aSg ? vrfa : vrfl;
if (vrfc == compVarRefSpan[1] && rfs != compVarRefSpan[1]) if (vrfc == cm.CompVarRef[1] && rfs != cm.CompVarRef[1])
{ {
predContext = 1; predContext = 1;
} }
else if (rfs == compVarRefSpan[1] && vrfc != compVarRefSpan[1]) else if (rfs == cm.CompVarRef[1] && vrfc != cm.CompVarRef[1])
{ {
predContext = 2; predContext = 2;
} }
@@ -215,15 +212,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
// Intra/Inter or Inter/Intra // Intra/Inter or Inter/Intra
ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value;
Span<sbyte> refFrameSpan = edgeMi.RefFrame.AsSpan();
if (!edgeMi.HasSecondRef()) if (!edgeMi.HasSecondRef())
{ {
predContext = 4 * (refFrameSpan[0] == Constants.LastFrame ? 1 : 0); predContext = 4 * (edgeMi.RefFrame[0] == Constants.LastFrame ? 1 : 0);
} }
else else
{ {
predContext = 1 + (refFrameSpan[0] == Constants.LastFrame || predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame ||
refFrameSpan[1] == Constants.LastFrame edgeMi.RefFrame[1] == Constants.LastFrame
? 1 ? 1
: 0); : 0);
} }
@@ -233,12 +229,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Inter/Inter // Inter/Inter
bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef();
bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef();
Span<sbyte> aRefFrameSpan = xd.AboveMi.Value.RefFrame.AsSpan(); sbyte above0 = xd.AboveMi.Value.RefFrame[0];
sbyte above0 = aRefFrameSpan[0]; sbyte above1 = xd.AboveMi.Value.RefFrame[1];
sbyte above1 = aRefFrameSpan[1]; sbyte left0 = xd.LeftMi.Value.RefFrame[0];
Span<sbyte> lRefFrameSpan = xd.LeftMi.Value.RefFrame.AsSpan(); sbyte left1 = xd.LeftMi.Value.RefFrame[1];
sbyte left0 = lRefFrameSpan[0];
sbyte left1 = lRefFrameSpan[1];
if (aboveHasSecond && leftHasSecond) if (aboveHasSecond && leftHasSecond)
{ {
@@ -287,10 +281,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
else else
{ {
Span<sbyte> refFrameSpan = edgeMi.RefFrame.AsSpan(); predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame ||
edgeMi.RefFrame[1] == Constants.LastFrame
predContext = 1 + (refFrameSpan[0] == Constants.LastFrame ||
refFrameSpan[1] == Constants.LastFrame
? 1 ? 1
: 0); : 0);
} }
@@ -329,22 +321,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
// Intra/Inter or Inter/Intra // Intra/Inter or Inter/Intra
ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value;
Span<sbyte> refFrameSpan = edgeMi.RefFrame.AsSpan();
if (!edgeMi.HasSecondRef()) if (!edgeMi.HasSecondRef())
{ {
if (refFrameSpan[0] == Constants.LastFrame) if (edgeMi.RefFrame[0] == Constants.LastFrame)
{ {
predContext = 3; predContext = 3;
} }
else else
{ {
predContext = 4 * (refFrameSpan[0] == Constants.GoldenFrame ? 1 : 0); predContext = 4 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ? 1 : 0);
} }
} }
else else
{ {
predContext = 1 + (2 * (refFrameSpan[0] == Constants.GoldenFrame || predContext = 1 + (2 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ||
refFrameSpan[1] == Constants.GoldenFrame edgeMi.RefFrame[1] == Constants.GoldenFrame
? 1 ? 1
: 0)); : 0));
} }
@@ -354,12 +345,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Inter/Inter // Inter/Inter
bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef();
bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef();
Span<sbyte> aRefFrameSpan = xd.AboveMi.Value.RefFrame.AsSpan(); sbyte above0 = xd.AboveMi.Value.RefFrame[0];
sbyte above0 = aRefFrameSpan[0]; sbyte above1 = xd.AboveMi.Value.RefFrame[1];
sbyte above1 = aRefFrameSpan[1]; sbyte left0 = xd.LeftMi.Value.RefFrame[0];
Span<sbyte> lRefFrameSpan = xd.LeftMi.Value.RefFrame.AsSpan(); sbyte left1 = xd.LeftMi.Value.RefFrame[1];
sbyte left0 = lRefFrameSpan[0];
sbyte left1 = lRefFrameSpan[1];
if (aboveHasSecond && leftHasSecond) if (aboveHasSecond && leftHasSecond)
{ {
@@ -418,20 +407,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
// One edge available // One edge available
ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value; ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value;
Span<sbyte> refFrameSpan = edgeMi.RefFrame.AsSpan();
if (!edgeMi.IsInterBlock() || (refFrameSpan[0] == Constants.LastFrame && !edgeMi.HasSecondRef())) if (!edgeMi.IsInterBlock() || (edgeMi.RefFrame[0] == Constants.LastFrame && !edgeMi.HasSecondRef()))
{ {
predContext = 2; predContext = 2;
} }
else if (!edgeMi.HasSecondRef()) else if (!edgeMi.HasSecondRef())
{ {
predContext = 4 * (refFrameSpan[0] == Constants.GoldenFrame ? 1 : 0); predContext = 4 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ? 1 : 0);
} }
else else
{ {
predContext = 3 * (refFrameSpan[0] == Constants.GoldenFrame || predContext = 3 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ||
refFrameSpan[1] == Constants.GoldenFrame edgeMi.RefFrame[1] == Constants.GoldenFrame
? 1 ? 1
: 0); : 0);
} }

View File

@@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
return (byte)BitUtils.RoundPowerOfTwo((prob1 * (256 - factor)) + (prob2 * factor), 8); return (byte)BitUtils.RoundPowerOfTwo((prob1 * (256 - factor)) + (prob2 * factor), 8);
} }
public static byte MergeProbs(byte preProb, ReadOnlySpan<uint> ct, uint countSat, uint maxUpdateFactor) public static byte MergeProbs(byte preProb, ref Array2<uint> ct, uint countSat, uint maxUpdateFactor)
{ {
byte prob = GetBinaryProb(ct[0], ct[1]); byte prob = GetBinaryProb(ct[0], ct[1]);
uint count = Math.Min(ct[0] + ct[1], countSat); uint count = Math.Min(ct[0] + ct[1], countSat);
@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private const int ModeMvCountSat = 20; private const int ModeMvCountSat = 20;
public static byte ModeMvMergeProbs(byte preProb, ReadOnlySpan<uint> ct) public static byte ModeMvMergeProbs(byte preProb, ref Array2<uint> ct)
{ {
uint den = ct[0] + ct[1]; uint den = ct[0] + ct[1];
if (den == 0) if (den == 0)
@@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
Array2<uint> ct = new(); Array2<uint> ct = new();
ct[0] = leftCount; ct[0] = leftCount;
ct[1] = rightCount; ct[1] = rightCount;
probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], ct.AsSpan()); probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], ref ct);
return leftCount + rightCount; return leftCount + rightCount;
} }

View File

@@ -164,7 +164,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
} }
public static void SetupDstPlanes( public static void SetupDstPlanes(
Span<MacroBlockDPlane> planes, ref Array3<MacroBlockDPlane> planes,
ref Surface src, ref Surface src,
int miRow, int miRow,
int miCol) int miCol)
@@ -205,11 +205,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
strides[1] = src.UvStride; strides[1] = src.UvStride;
strides[2] = src.UvStride; strides[2] = src.UvStride;
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; ++i) for (int i = 0; i < Constants.MaxMbPlane; ++i)
{ {
ref MacroBlockDPlane pd = ref planeSpan[i]; ref MacroBlockDPlane pd = ref xd.Plane[i];
SetupPredPlanes(ref pd.Pre[idx], buffers[i], strides[i], miRow, miCol, sf, pd.SubsamplingX, SetupPredPlanes(ref pd.Pre[idx], buffers[i], strides[i], miRow, miCol, sf, pd.SubsamplingX,
pd.SubsamplingY); pd.SubsamplingY);
} }

View File

@@ -1,5 +1,4 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{ {
@@ -30,15 +29,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
ModeRefDeltaEnabled = true; ModeRefDeltaEnabled = true;
ModeRefDeltaUpdate = true; ModeRefDeltaUpdate = true;
Span<sbyte> refDeltasSpan = RefDeltas.AsSpan(); RefDeltas[Constants.IntraFrame] = 1;
Span<sbyte> modeDeltasSpan = ModeDeltas.AsSpan(); RefDeltas[Constants.LastFrame] = 0;
RefDeltas[Constants.GoldenFrame] = -1;
refDeltasSpan[Constants.IntraFrame] = 1; RefDeltas[Constants.AltRefFrame] = -1;
refDeltasSpan[Constants.LastFrame] = 0; ModeDeltas[0] = 0;
refDeltasSpan[Constants.GoldenFrame] = -1; ModeDeltas[1] = 0;
refDeltasSpan[Constants.AltRefFrame] = -1;
modeDeltasSpan[0] = 0;
modeDeltasSpan[1] = 0;
} }
} }
} }

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Common;
using Ryujinx.Graphics.Video; using Ryujinx.Graphics.Video;
using System;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{ {
@@ -148,12 +147,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public void SetupBlockPlanes(int ssX, int ssY) public void SetupBlockPlanes(int ssX, int ssY)
{ {
Span<MacroBlockDPlane> planeSpan = Plane.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; i++) for (int i = 0; i < Constants.MaxMbPlane; i++)
{ {
planeSpan[i].SubsamplingX = i != 0 ? ssX : 0; Plane[i].SubsamplingX = i != 0 ? ssX : 0;
planeSpan[i].SubsamplingY = i != 0 ? ssY : 0; Plane[i].SubsamplingY = i != 0 ? ssY : 0;
} }
} }
@@ -162,15 +159,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
int aboveIdx = miCol * 2; int aboveIdx = miCol * 2;
int leftIdx = (miRow * 2) & 15; int leftIdx = (miRow * 2) & 15;
Span<MacroBlockDPlane> planeSpan = Plane.AsSpan();
Span<ArrayPtr<sbyte>> aboveContextSpan = AboveContext.AsSpan();
Span<Array16<sbyte>> leftContextSpan = LeftContext.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; ++i) for (int i = 0; i < Constants.MaxMbPlane; ++i)
{ {
ref MacroBlockDPlane pd = ref planeSpan[i]; ref MacroBlockDPlane pd = ref Plane[i];
pd.AboveContext = aboveContextSpan[i].Slice(aboveIdx >> pd.SubsamplingX); pd.AboveContext = AboveContext[i].Slice(aboveIdx >> pd.SubsamplingX);
pd.LeftContext = new ArrayPtr<sbyte>(ref leftContextSpan[i][leftIdx >> pd.SubsamplingY], pd.LeftContext = new ArrayPtr<sbyte>(ref LeftContext[i][leftIdx >> pd.SubsamplingY],
16 - (leftIdx >> pd.SubsamplingY)); 16 - (leftIdx >> pd.SubsamplingY));
} }
} }
@@ -189,11 +182,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public unsafe void DecResetSkipContext() public unsafe void DecResetSkipContext()
{ {
Span<MacroBlockDPlane> planeSpan = Plane.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; i++) for (int i = 0; i < Constants.MaxMbPlane; i++)
{ {
ref MacroBlockDPlane pd = ref planeSpan[i]; ref MacroBlockDPlane pd = ref Plane[i];
MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W); MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W);
MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H); MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H);
} }

Some files were not shown because too many files have changed in this diff Show More