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
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:
name: Release for ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
@@ -43,6 +91,16 @@ jobs:
- name: Overwrite csc problem matcher
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
id: version_info
@@ -102,10 +160,10 @@ jobs:
chmod +x Ryujinx.sh Ryujinx
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
shell: bash
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
run: |
@@ -144,7 +202,34 @@ 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"
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:
name: Release MacOS universal
@@ -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
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:
name: Create GitLab Release
runs-on: ubuntu-24.04
needs:
- tag
- macos_release
- release
steps:
- uses: actions/checkout@v4
- name: Get version info
id: version_info
run: |
@@ -232,18 +330,6 @@ jobs:
env:
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
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 }})"
- 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 }}'
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 }})**"

View File

@@ -14,6 +14,38 @@ env:
RELEASE: 1
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:
name: Release for ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
@@ -96,7 +128,7 @@ jobs:
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
run: |
@@ -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"
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:
name: Release MacOS universal
runs-on: ubuntu-24.04
@@ -193,11 +249,26 @@ jobs:
run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
- 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:
name: Create GitLab Release
runs-on: ubuntu-24.04
needs:
- tag
- macos_release
- release
steps:
@@ -223,12 +294,4 @@ jobs:
- name: Create release
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateReleaseFromGenericPackageFiles "Ryubing|${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}|${{ steps.version_info.outputs.build_version }}|msd:${{ 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 }}'
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 }})**"

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.
## [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
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
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
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
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
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
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.72](<https://git.ryujinx.app/ryubing/ryujinx/-/tags/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>)
## [1.2.72](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.72>) - 2024-11-03
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:
- Add DebugMouse HID device.
- Fixes "Clock Tower Rewind" crashing while loading.
@@ -32,7 +35,7 @@ PRs [#163](<https://web.archive.org/web/20241123015123/https://github.com/GreemD
### misc:
- 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:
- Compile the native libraries into the Ryujinx executable.
- 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`.
- Code cleanups & simplifications.
## [1.2.67](<https://git.ryujinx.app/ryubing/ryujinx/-/tags/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>)
## [1.2.67](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.67>) - 2024-11-01
PRs [#36](<https://github.com/GreemDev/Ryujinx/pull/36>), [#135](<https://github.com/GreemDev/Ryujinx/pull/135>)
### GUI:
- 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.
## [1.2.64](https://git.ryujinx.app/ryubing/ryujinx/-/tags/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)~~
## [1.2.64](https://github.com/GreemDev/Ryujinx/releases/tag/1.2.64) - 2024-10-30
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:
- 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.
@@ -71,14 +74,14 @@ PRs [#92](https://web.archive.org/web/20241118052724/https://github.com/GreemDev
## 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:
- fr_FR:
- Add missing translations for new features & fix a couple wrong ones.
- Fix Ignore Missing Services / Ignore Applet tooltip.
## 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:
- Automatically remove invalid DLC & updates as part of autoload.
- 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.
## 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:
- 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
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
### GUI:
@@ -127,14 +130,14 @@ Thanks MutantAura :D
- Remove graphics backend / GPU name event logic in favor of a single init function.
## 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!
### i18n:
- th_TH (Thai): Added missing translations, reduce transliterated words, fix grammar.
## 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 Вова С!
### GUI:
@@ -148,30 +151,30 @@ Thanks Вова С!
- Should prevent crashing on config loads in some circumstances.
## 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:
- zh_CH (Simplified Chinese): Add some missing translations.
## 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!
### GUI:
- Set the default controller to the Pro Controller.
## 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:
- 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
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:
- Replace "expand DRAM" option with a DRAM size dropdown.
- Allows for using mods which require a ridiculous amount of memory to allocate from.
## 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:
- Fix duplicate controller names when 2 controllers of the same type are connected.
### 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
### GUI/INFRA/MISC:
- 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.
- 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="CommunityToolkit.Mvvm" Version="8.4.0"/>
<PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="DynamicData" Version="9.4.1" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="9.0.4" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<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.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<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.UpdateClient" Version="1.0.44" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
<PackageVersion Include="Gommon" Version="2.7.2.1" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
<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="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />

View File

@@ -7,8 +7,8 @@
# 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 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 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/github/v/release/Ryubing/Canary-Releases?label=canary)](https://github.com/Ryubing/Canary-Releases/releases/latest)
<br>
<a href="https://discord.gg/PEuzjrFXUA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
@@ -31,7 +31,7 @@
<br>
This is not a Ryujinx revival project. This is not a Phoenix project.
<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 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**.
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.
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.
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
@@ -111,7 +111,7 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY
## 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.
- [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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
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}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@@ -86,9 +84,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\canary.yml = .github\workflows\canary.yml
Directory.Packages.props = Directory.Packages.props
.github\workflows\release.yml = .github\workflows\release.yml
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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"
appimagetool -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
appimagetool --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
-u "$UFLAG" \
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"
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
echo "Starting signing process"
if ! [ -x "$(command -v codesign)" ];
then
if ! [ -x "$(command -v rcodesign)" ];
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
fi
# cargo install apple-codesign
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"
else
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"
fi

View File

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

View File

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

View File

@@ -188,8 +188,6 @@
01003DD00BFEE000,"Airheart - Tales of broken Wings",,playable,2021-02-26 15:20:27
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
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
0100D4C00EE0C000,"Akuarium",slow,playable,2020-12-12 23:43:36
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
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
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
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
@@ -1097,7 +1095,6 @@
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
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
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
@@ -1128,7 +1125,6 @@
0100034012606000,"Family Mysteries: Poisonous Promises",audio;crash,menus,2021-11-26 12:35:06
010017C012726000,"Fantasy Friends",,playable,2022-10-17 19:42:39
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
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
@@ -1243,8 +1239,6 @@
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
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
0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36
010038A007AA4000,"FruitFall Crush",,playable,2020-10-20 11:33:33
@@ -1440,7 +1434,6 @@
0100C2700E338000,"Heroland",,playable,2020-08-05 15:35:39
01007AC00E012000,"HexaGravity",,playable,2021-05-28 13:47:48
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
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
@@ -1450,7 +1443,6 @@
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
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
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38
@@ -1525,7 +1517,6 @@
010095C016C14000,"Iridium",,playable,2022-08-05 23:19:53
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
010059801B736000,"IronFall: Invasion",,playable,2025-07-30 11:42:30
01005270118D6000,"Iron Wings",slow,ingame,2022-08-07 08:32:57
01004DB003E6A000,"IRONCAST",,playable,2021-01-13 13:54:29
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
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
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
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
@@ -2265,7 +2256,6 @@
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
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
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
@@ -2314,7 +2304,6 @@
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
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
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
@@ -2446,7 +2435,6 @@
0100E9C010EA8000,"Rise of Insanity",,playable,2020-08-30 15:42:14
01006BA00E652000,"Rise: Race The Future",,playable,2021-02-27 13:29:06
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
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
@@ -2580,7 +2568,6 @@
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
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
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
@@ -2758,7 +2745,6 @@
01005D701264A000,"SpyHack",,playable,2021-04-15 10:53:51
010077B00E046000,"Spyro™ Reignited Trilogy",nvdec;UE4,playable,2022-09-11 18:38:33
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
0100FCD0102EC000,"Squidlit",,playable,2020-08-06 12:38:32
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
01002100137BA000,"Stardash",,playable,2021-01-21 16:31: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
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
@@ -2979,7 +2965,6 @@
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
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
01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45
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
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
0100BA5013E52000,"The Falconeer: Warrior Edition",,playable,2025-07-30 12:04:50
01002DD00AF9E000,"The Fall",gpu,ingame,2020-05-31 23:31:16
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
@@ -3031,7 +3015,6 @@
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
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
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
@@ -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
0100752011628000,"TTV2",,playable,2020-11-27 13:21:36
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
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
@@ -3234,8 +3216,6 @@
0100592005164000,"UNBOX: Newbie's Adventure",UE4,playable,2022-08-29 13:12:56
01002D900C5E4000,"Uncanny Valley",nvdec,playable,2021-06-04 13:28:45
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
010080B00AD66000,"Undertale",,playable,2022-08-31 17:31:46
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>
<clear />
<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="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>
<!-- Define mappings by adding package patterns beneath the target source. -->
<!-- Ryujinx.LibHac packages will be restored from LibHacAlpha,
everything else from nuget.org. -->
<packageSourceMapping>
<!-- 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">
<package pattern="*" />
</packageSource>
<packageSource key="Ryujinx.UpdateClient">
<package pattern="Ryujinx.UpdateClient" />
<package pattern="Ryujinx.Systems.Update.Common" />
</packageSource>
<packageSource key="LibHacAlpha">
<package pattern="Ryujinx.LibHac" />
</packageSource>

View File

@@ -143,12 +143,6 @@ namespace ARMeilleure.Instructions
public static void EmitCall(ArmEmitterContext context, ulong immediate)
{
if (context.IsSingleStep)
{
context.Return(Const(immediate));
return;
}
bool isRecursive = immediate == context.EntryAddress;
if (isRecursive)
@@ -163,24 +157,12 @@ namespace ARMeilleure.Instructions
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)
{
if (isReturn || context.IsSingleStep)
if (isReturn)
{
if (target.Type == OperandType.I32)
{

View File

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

View File

@@ -12,7 +12,6 @@ namespace ARMeilleure
public static bool AllowLcqInFunctionTable { 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 UseArm64AesIfAvailable { get; set; } = true;

View File

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

View File

@@ -22,12 +22,6 @@ namespace ARMeilleure.State
public ulong ExclusiveValueHigh;
public int Running;
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();
@@ -45,11 +39,6 @@ namespace ARMeilleure.State
public ulong GetPc()
{
if (Optimizations.EnableDebugging)
{
return GetStorage().DebugPrecisePc;
}
// TODO: More precise tracking of PC value.
return GetStorage().DispatchAddress;
}
@@ -279,11 +268,6 @@ namespace ARMeilleure.State
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)
{
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 HasPtc { get; }
public Aarch32Mode Mode { get; }
public bool IsSingleStep { get; }
private int _ifThenBlockStateIndex = 0;
private Condition[] _ifThenBlockState = [];
@@ -67,8 +66,7 @@ namespace ARMeilleure.Translation
ulong entryAddress,
bool highCq,
bool hasPtc,
Aarch32Mode mode,
bool isSingleStep)
Aarch32Mode mode)
{
Memory = memory;
CountTable = countTable;
@@ -78,7 +76,6 @@ namespace ARMeilleure.Translation
HighCq = highCq;
HasPtc = hasPtc;
Mode = mode;
IsSingleStep = isSingleStep;
_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 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 BackupDir = "1";
@@ -193,7 +193,7 @@ namespace ARMeilleure.Translation.PTC
_infosStream.Seek(0L, SeekOrigin.Begin);
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);
foreach (ulong address in blacklist)
@@ -303,13 +303,6 @@ namespace ARMeilleure.Translation.PTC
return false;
}
if (outerHeader.DebuggerMode != Optimizations.EnableDebugging)
{
InvalidateCompressedStream(compressedStream);
return false;
}
nint intPtr = nint.Zero;
try
@@ -486,7 +479,6 @@ namespace ARMeilleure.Translation.PTC
MemoryManagerMode = GetMemoryManagerMode(),
OSPlatform = GetOSPlatform(),
Architecture = (uint)RuntimeInformation.ProcessArchitecture,
DebuggerMode = Optimizations.EnableDebugging,
UncompressedStreamSize =
(long)Unsafe.SizeOf<InnerHeader>() +
@@ -1076,7 +1068,7 @@ namespace ARMeilleure.Translation.PTC
return osPlatform;
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 87*/)]
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
private struct OuterHeader
{
public ulong Magic;
@@ -1088,7 +1080,6 @@ namespace ARMeilleure.Translation.PTC
public byte MemoryManagerMode;
public uint OSPlatform;
public uint Architecture;
public bool DebuggerMode;
public long UncompressedStreamSize;

View File

@@ -119,25 +119,7 @@ namespace ARMeilleure.Translation
NativeInterface.RegisterThread(context, Memory, this);
if (Optimizations.EnableDebugging)
{
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)
if (Optimizations.UseUnmanagedDispatchLoop)
{
Stubs.DispatchLoop(context.NativeContextPtr, address);
}
@@ -193,7 +175,7 @@ namespace ARMeilleure.Translation
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);
@@ -204,8 +186,6 @@ namespace ARMeilleure.Translation
return address;
}
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
{
if (!Functions.TryGetValue(address, out TranslatedFunction func))
@@ -249,8 +229,7 @@ namespace ARMeilleure.Translation
address,
highCq,
_ptc.State != PtcState.Disabled,
mode: Aarch32Mode.User,
isSingleStep: singleStep);
mode: Aarch32Mode.User);
Logger.StartPass(PassName.Decoding);
@@ -388,13 +367,9 @@ namespace ARMeilleure.Translation
if (block.Exit)
{
// Return to managed rather than tail call.
bool useReturns = Optimizations.EnableDebugging;
if (Optimizations.EnableDebugging)
{
EmitDebugPrecisePcUpdate(context, block.Address);
}
// Left option here as it may be useful if we need to return to managed rather than tail call in
// future. (eg. for debug)
bool useReturns = false;
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;
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
@@ -519,14 +489,6 @@ namespace ARMeilleure.Translation
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)
{
ulong[] overlapAddresses = [];

View File

@@ -81,14 +81,14 @@ namespace Ryujinx.Audio.Renderer.Dsp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
{
if ((uint)index >= (uint)coefficients.Length)
if ((uint)index < (uint)coefficients.Length)
{
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
return 0;
return coefficients[index];
}
return coefficients[index];
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
return 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

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

View File

@@ -39,14 +39,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
OutputBufferIndex = outputBufferIndex;
SampleRate = serverState.SampleRate;
Pitch = serverState.Pitch;
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
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);
}

View File

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

View File

@@ -42,14 +42,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = 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++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
}
}
@@ -174,12 +171,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
Span<float> lastSamplesSpan = statistics.LastSamples.AsSpan();
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

@@ -52,14 +52,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex);
SampleRate = serverState.SampleRate;
Pitch = serverState.Pitch;
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
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);
}

View File

@@ -42,14 +42,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = 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++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
}
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];
Span<float> depopBuffer = DepopBuffer.Span;
Span<float> lastSamplesSpan = state.LastSamples.AsSpan();
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

@@ -34,12 +34,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
SessionId = sessionId;
InputCount = sink.Parameter.InputCount;
InputBufferIndices = new ushort[InputCount];
Span<byte> inputSpan = sink.Parameter.Input.AsSpan();
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)

View File

@@ -37,14 +37,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = 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++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
}
}

View File

@@ -48,14 +48,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = 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++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[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];
Span<float> inputMaxSpan = statistics.InputMax.AsSpan();
Span<float> compressionGainMinSpan = statistics.CompressionGainMin.AsSpan();
inputMaxSpan[channelIndex] = Math.Max(inputMaxSpan[channelIndex], sampleInputMax);
compressionGainMinSpan[channelIndex] = Math.Min(compressionGainMinSpan[channelIndex], compressionGain);
statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], compressionGain);
}
}
}

View File

@@ -79,10 +79,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
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++)
{
ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndices[i]);
@@ -91,13 +87,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float volume0 = Volume0[i];
float volume1 = Volume1[i];
ref VoiceUpdateState state = ref State.Span[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
{
lastSamplesSpan[i] = 0;
state.LastSamples[i] = 0;
}
}
}

View File

@@ -42,12 +42,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Pitch = serverState.Pitch;
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
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);
}

View File

@@ -42,12 +42,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Pitch = serverState.Pitch;
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
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);
}

View File

@@ -65,14 +65,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = 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++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
}
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour

View File

@@ -63,14 +63,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = 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++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
}
IsLongSizePreDelaySupported = isLongSizePreDelaySupported;

View File

@@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
{
int tempBufferIndex = 0;
if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) != DecodingBehaviour.SkipPitchAndSampleRateConversion)
if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
{
voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer);
tempBufferIndex += pitchMaxLength;
@@ -208,7 +208,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
break;
}
if ((info.DecodingBehaviour & DecodingBehaviour.PlayedSampleCountResetWhenLooping) == DecodingBehaviour.PlayedSampleCountResetWhenLooping)
if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.PlayedSampleCountResetWhenLooping))
{
playedSampleCount = 0;
}
@@ -222,7 +222,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
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++)
{

View File

@@ -41,12 +41,11 @@ namespace Ryujinx.Audio.Renderer.Dsp
}
Array20<float> result = new();
Span<float> resultSpan = result.AsSpan();
for (int i = 0; i < FilterBankLength; i++)
{
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;
@@ -79,9 +78,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
Debug.Assert(state.History.Length == HistoryLength);
Debug.Assert(bank.Length == FilterBankLength);
Span<float> bankSpan = bank.AsSpan();
Span<float> historySpan = state.History.AsSpan();
int curIdx = 0;
if (Vector.IsHardwareAccelerated)
@@ -92,15 +88,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
while (curIdx < stopIdx)
{
result += Vector.Dot(
new Vector<float>(bankSpan[curIdx..(curIdx + Vector<float>.Count)]),
new Vector<float>(historySpan[curIdx..(curIdx + Vector<float>.Count)]));
new Vector<float>(bank.AsSpan().Slice(curIdx, Vector<float>.Count)),
new Vector<float>(state.History.AsSpan().Slice(curIdx, Vector<float>.Count)));
curIdx += Vector<float>.Count;
}
}
while (curIdx < FilterBankLength)
{
result += bankSpan[curIdx] * historySpan[curIdx];
result += bank[curIdx] * state.History[curIdx];
curIdx++;
}

View File

@@ -141,20 +141,18 @@ namespace Ryujinx.Audio.Renderer.Server
{
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
Span<BiquadFilterParameter> biquadFiltersSpan = voiceState.BiquadFilters.AsSpan();
if (supportsOptimizedPath && biquadFiltersSpan[0].Enable && biquadFiltersSpan[1].Enable)
if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
{
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
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
{
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)
{
@@ -313,15 +311,12 @@ namespace Ryujinx.Audio.Renderer.Server
{
int nodeId = voiceState.NodeId;
uint channelsCount = voiceState.ChannelsCount;
Span<int> channelResourceIdsSpan = voiceState.ChannelResourceIds.AsSpan();
Span<BiquadFilterParameter> biquadFiltersSpan = voiceState.BiquadFilters.AsSpan();
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;
@@ -481,7 +476,7 @@ namespace Ryujinx.Audio.Renderer.Server
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)
{
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++)
{
if (volumesSpan[i] != 0.0f)
if (effect.Parameter.Volumes[i] != 0.0f)
{
_commandBuffer.GenerateMix(
(uint)bufferOffset + inputSpan[i],
(uint)bufferOffset + outputSpan[i],
(uint)bufferOffset + effect.Parameter.Input[i],
(uint)bufferOffset + effect.Parameter.Output[i],
nodeId,
volumesSpan[i]);
effect.Parameter.Volumes[i]);
}
}
}
@@ -563,10 +554,6 @@ namespace Ryujinx.Audio.Renderer.Server
{
int i = 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--)
{
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
@@ -584,8 +571,8 @@ namespace Ryujinx.Audio.Renderer.Server
_commandBuffer.GenerateAuxEffect(
bufferOffset,
inputSpan[i],
outputSpan[i],
effect.Parameter.Input[i],
effect.Parameter.Output[i],
ref effect.State,
effect.IsEnabled,
effect.Parameter.BufferStorageSize,
@@ -632,9 +619,6 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId)
{
Debug.Assert(effect.Type == EffectType.BiquadFilter);
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
if (effect.IsEnabled)
{
@@ -655,8 +639,8 @@ namespace Ryujinx.Audio.Renderer.Server
(int)bufferOffset,
ref parameter,
effect.State.Slice(i, 1),
inputSpan[i],
outputSpan[i],
effect.Parameter.Input[i],
effect.Parameter.Output[i],
needInitialization,
nodeId);
}
@@ -665,8 +649,8 @@ namespace Ryujinx.Audio.Renderer.Server
{
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
{
uint inputBufferIndex = bufferOffset + inputSpan[i];
uint outputBufferIndex = bufferOffset + outputSpan[i];
uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i];
uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i];
// If the input and output isn't the same, generate a command.
if (inputBufferIndex != outputBufferIndex)
@@ -717,8 +701,6 @@ namespace Ryujinx.Audio.Renderer.Server
{
int i = 0;
uint writeOffset = 0;
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
{
@@ -737,7 +719,7 @@ namespace Ryujinx.Audio.Renderer.Server
_commandBuffer.GenerateCaptureEffect(
bufferOffset,
inputSpan[i],
effect.Parameter.Input[i],
effect.State.SendBufferInfo,
effect.IsEnabled,
effect.Parameter.BufferStorageSize,

View File

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

View File

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

View File

@@ -126,9 +126,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
_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 bState = ref GetState(b);
@@ -143,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
return result;
});
// sortedVoicesTemp.CopyTo(_sortedVoices.Span);
sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span);
}
}
}

View File

@@ -219,17 +219,15 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// </summary>
private void InitializeWaveBuffers()
{
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
for (int i = 0; i < waveBuffersSpan.Length; i++)
for (int i = 0; i < WaveBuffers.Length; i++)
{
waveBuffersSpan[i].StartSampleOffset = 0;
waveBuffersSpan[i].EndSampleOffset = 0;
waveBuffersSpan[i].ShouldLoop = false;
waveBuffersSpan[i].IsEndOfStream = false;
waveBuffersSpan[i].BufferAddressInfo.Setup(0, 0);
waveBuffersSpan[i].ContextAddressInfo.Setup(0, 0);
waveBuffersSpan[i].IsSendToAudioProcessor = true;
WaveBuffers[i].StartSampleOffset = 0;
WaveBuffers[i].EndSampleOffset = 0;
WaveBuffers[i].ShouldLoop = false;
WaveBuffers[i].IsEndOfStream = false;
WaveBuffers[i].BufferAddressInfo.Setup(0, 0);
WaveBuffers[i].ContextAddressInfo.Setup(0, 0);
WaveBuffers[i].IsSendToAudioProcessor = true;
}
}
@@ -448,13 +446,10 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
}
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++)
{
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>
private void ResetResources(VoiceContext context)
{
Span<int> channelResourceIdsSpan = ChannelResourceIds.AsSpan();
for (int i = 0; i < ChannelsCount; i++)
{
int channelResourceId = channelResourceIdsSpan[i];
int channelResourceId = ChannelResourceIds[i];
ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId);
@@ -566,12 +559,10 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
private void FlushWaveBuffers(uint waveBufferCount, Memory<VoiceUpdateState>[] voiceUpdateStates, uint channelCount)
{
uint waveBufferIndex = WaveBuffersIndex;
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
for (int i = 0; i < waveBufferCount; i++)
{
waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor = true;
WaveBuffers[(int)waveBufferIndex].IsSendToAudioProcessor = true;
for (int j = 0; j < channelCount; j++)
{
@@ -600,18 +591,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
FlushWaveBufferCount = 0;
}
Span<WaveBuffer> waveBuffersSpan;
switch (PlayState)
{
case PlayState.Started:
waveBuffersSpan = WaveBuffers.AsSpan();
for (int i = 0; i < waveBuffersSpan.Length; i++)
for (int i = 0; i < WaveBuffers.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++)
{
@@ -620,7 +607,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
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;
case PlayState.Stopping:
waveBuffersSpan = WaveBuffers.AsSpan();
for (int i = 0; i < waveBuffersSpan.Length; i++)
for (int i = 0; i < WaveBuffers.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++)
{
@@ -717,11 +702,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
Span<int> channelResourceIdsSpan = ChannelResourceIds.AsSpan();
for (int i = 0; i < ChannelsCount; i++)
{
voiceUpdateStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]);
voiceUpdateStates[i] = context.GetUpdateStateForDsp(ChannelResourceIds[i]);
}
return UpdateParametersForCommandGeneration(voiceUpdateStates);

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.BuildValidationTasks
LocalesJson json;
if (isGitRunner && data.Contains("\r\n"))
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, 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
{
@@ -86,7 +86,7 @@ namespace Ryujinx.BuildValidationTasks
}
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);
@@ -102,7 +102,6 @@ namespace Ryujinx.BuildValidationTasks
struct LocalesJson
{
public Dictionary<string, string> Info { get; set; }
public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; }
}

View File

@@ -10,18 +10,8 @@
<Exec WorkingDirectory="$(ProjectDir)bin\Debug\$(TargetFramework)\"
Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;"
ConsoleToMsBuild="true"
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'"/>
Condition="'$(RuntimeIdentifier)' == ''"
/>
</Target>
</Project>
</Project>

View File

@@ -14,13 +14,12 @@ namespace Ryujinx.Common.Collections
/// Adds a new node into the tree.
/// </summary>
/// <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>
public void Add(T node, T parent = null)
public void Add(T node)
{
ArgumentNullException.ThrowIfNull(node);
Insert(node, parent);
Insert(node);
}
/// <summary>
@@ -77,11 +76,9 @@ namespace Ryujinx.Common.Collections
/// Inserts a new node into the tree.
/// </summary>
/// <param name="node">Node to be inserted</param>
/// <param name="parent">Node to be inserted under</param>
private void Insert(T node, T parent = null)
private void Insert(T node)
{
T newNode = parent != null ? InsertWithParent(node, parent) : BSTInsert(node);
T newNode = BSTInsert(node);
RestoreBalanceAfterInsertion(newNode);
}
@@ -125,77 +122,10 @@ namespace Ryujinx.Common.Collections
else if (newNode.CompareTo(parent) < 0)
{
parent.Left = newNode;
newNode.Successor = parent;
if (parent.Predecessor != null)
{
newNode.Predecessor = parent.Predecessor;
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++;
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++;
@@ -229,7 +159,7 @@ namespace Ryujinx.Common.Collections
}
else
{
T element = nodeToDelete.Successor;
T element = Minimum(RightOf(nodeToDelete));
child = RightOf(element);
parent = ParentOf(element);
@@ -257,9 +187,6 @@ namespace Ryujinx.Common.Collections
element.Left = old.Left;
element.Right = old.Right;
element.Parent = old.Parent;
element.Predecessor = old.Predecessor;
if (element.Predecessor != null)
element.Predecessor.Successor = element;
if (ParentOf(old) == null)
{
@@ -314,11 +241,6 @@ namespace Ryujinx.Common.Collections
{
RestoreBalanceAfterRemoval(child);
}
if (old.Successor != null)
old.Successor.Predecessor = old.Predecessor;
if (old.Predecessor != null)
old.Predecessor.Successor = old.Successor;
return old;
}

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
#nullable enable
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -11,126 +10,11 @@ namespace Ryujinx.Common.Memory
{
/// <summary>
/// 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>
/// <typeparam name="T">The type of item to store.</typeparam>
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 T[]? _array;
@@ -141,7 +25,7 @@ namespace Ryujinx.Common.Memory
private MemoryOwner(int length)
{
_length = length;
_array = ArrayPooling.Get(length);
_array = ArrayPool<T>.Shared.Rent(length);
}
/// <summary>
@@ -240,7 +124,7 @@ namespace Ryujinx.Common.Memory
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.Threading;
using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers;
@@ -43,13 +42,10 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
public int GetOrReserve(int threadId, T initial)
{
// Try get a match first.
Span<int> threadIdsSpan = ThreadIds.AsSpan();
Span<T> structsSpan = Structs.AsSpan();
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)
{
@@ -61,11 +57,11 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
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)
{
structsSpan[i] = initial;
Structs[i] = initial;
return i;
}
}

View File

@@ -2,18 +2,8 @@ namespace Ryujinx.Common
{
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 string DefaultLanPlayWebHost = DefaultLanPlayHost;
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";
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
}
}

View File

@@ -93,7 +93,6 @@ namespace Ryujinx.Common
//The Pokémon Franchise
"0100f4300bf2c000", // New Pokémon Snap
"0100000011d90000", // Pokémon Brilliant Diamond
"010008c01e742000", // Pokémon Friends
"01001f5010dfa000", // Pokémon Legends: Arceus
"01003d200baa2000", // Pokémon Mystery Dungeon - Rescue Team DX
"0100a3d008c5c000", // Pokémon Scarlet
@@ -134,6 +133,7 @@ namespace Ryujinx.Common
"0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze
"0100ed000d390000", // Dr. Kawashima's Brain Training
"010067b017588000", // Endless Ocean Luminous
"0100d2f00d5c0000", // Nintendo Switch Sports
"01006b5012b32000", // Part Time UFO
"0100704000B3A000", // Snipperclips
"01006a800016e000", // Super Smash Bros. Ultimate
@@ -169,8 +169,6 @@ namespace Ryujinx.Common
"010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game
"010096f00ff22000", // Borderlands 2: Game of the Year Edition
"010007400ff24000", // Borderlands: The Pre-Sequel Ultimate Edition
"0100c6800b934000", // Brawlhalla
"0100dbf01000a000", // Burnout Paradise Remastered
"0100744001588000", // Cars 3: Driven to Win
@@ -182,7 +180,6 @@ namespace Ryujinx.Common
"01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy
"010013c00e930000", // Hollow Knight: Silksong
"010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures
"0100d71004694000", // Minecraft
@@ -197,7 +194,6 @@ namespace Ryujinx.Common
"01008d100d43e000", // Saints Row IV
"0100de600beee000", // Saints Row: The Third - The Full Package
"01001180021fa000", // Shovel Knight: Specter of Torment
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
"0100e65002bb8000", // Stardew Valley
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
"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
{
/// <inheritdoc/>
public ulong Pc
{
get
{
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
if (currentEl == (uint)ExceptionLevel.EL1h)
{
return _impl.ElrEl1;
}
return _impl.Pc;
}
}
public ulong Pc => _impl.ElrEl1;
/// <inheritdoc/>
public long TpidrEl0
@@ -59,9 +48,6 @@ namespace Ryujinx.Cpu.AppleHv
set => _impl.Fpsr = value;
}
/// <inheritdoc/>
public ulong ThreadUid { get; set; }
/// <inheritdoc/>
public bool IsAarch32
{
@@ -81,7 +67,6 @@ namespace Ryujinx.Cpu.AppleHv
private readonly ICounter _counter;
private readonly IHvExecutionContext _shadowContext;
private IHvExecutionContext _impl;
private int _shouldStep;
private readonly ExceptionCallbacks _exceptionCallbacks;
@@ -118,11 +103,6 @@ namespace Ryujinx.Cpu.AppleHv
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
}
private void StepHandler()
{
_exceptionCallbacks.StepCallback?.Invoke(this);
}
private void SupervisorCallHandler(ulong address, int imm)
{
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
@@ -147,30 +127,6 @@ namespace Ryujinx.Cpu.AppleHv
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/>
public void StopRunning()
{
@@ -186,22 +142,6 @@ namespace Ryujinx.Cpu.AppleHv
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();
HvExitReason reason = vcpu.ExitInfo->Reason;
@@ -269,20 +209,6 @@ namespace Ryujinx.Cpu.AppleHv
SupervisorCallHandler(elr - 4UL, id);
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
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:
throw new Exception($"Unhandled guest exception {ec}.");
}
@@ -293,7 +219,10 @@ namespace Ryujinx.Cpu.AppleHv
// TODO: Invalidate only the range that was modified?
return HvAddressSpace.KernelRegionTlbiEretAddress;
}
return HvAddressSpace.KernelRegionEretAddress;
else
{
return HvAddressSpace.KernelRegionEretAddress;
}
}
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 ulong ThreadUid { get; set; }
private readonly ulong[] _x;
private readonly V128[] _v;
@@ -48,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
{
_v[index] = value;
}
public void RequestInterrupt()
{
}
public bool GetAndClearInterruptRequested()
{
return false;
}
}
}

View File

@@ -2,7 +2,6 @@ using ARMeilleure.State;
using Ryujinx.Memory;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv
{
@@ -14,8 +13,6 @@ namespace Ryujinx.Cpu.AppleHv
private static readonly SetSimdFpReg _setSimdFpReg;
private static readonly nint _setSimdFpRegNativePtr;
public ulong ThreadUid { get; set; }
static HvExecutionContextVcpu()
{
// .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 int _interruptRequested;
public HvExecutionContextVcpu(ulong vcpu)
{
@@ -184,16 +180,8 @@ namespace Ryujinx.Cpu.AppleHv
public void RequestInterrupt()
{
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
{
ulong vcpu = _vcpu;
HvApi.hv_vcpus_exit(ref vcpu, 1);
}
}
public bool GetAndClearInterruptRequested()
{
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
ulong vcpu = _vcpu;
HvApi.hv_vcpus_exit(ref vcpu, 1);
}
}
}

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using ARMeilleure.State;
using System;
using System.Threading;
namespace Ryujinx.Cpu
{
@@ -47,11 +46,6 @@ namespace Ryujinx.Cpu
/// </summary>
bool IsAarch32 { get; set; }
/// <summary>
/// Thread UID.
/// </summary>
public ulong ThreadUid { get; set; }
/// <summary>
/// Indicates whenever the CPU is still running code.
/// </summary>
@@ -114,23 +108,5 @@ namespace Ryujinx.Cpu
/// If you only need to pause the thread temporarily, use <see cref="RequestInterrupt"/> instead.
/// </remarks>
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.State;
using ExecutionContext = ARMeilleure.State.ExecutionContext;
namespace Ryujinx.Cpu.Jit
{
@@ -54,13 +53,6 @@ namespace Ryujinx.Cpu.Jit
set => _impl.IsAarch32 = value;
}
/// <inheritdoc/>
public ulong ThreadUid
{
get => _impl.ThreadUid;
set => _impl.ThreadUid = value;
}
/// <inheritdoc/>
public bool Running => _impl.Running;
@@ -73,7 +65,6 @@ namespace Ryujinx.Cpu.Jit
counter,
InterruptHandler,
BreakHandler,
StepHandler,
SupervisorCallHandler,
UndefinedHandler);
@@ -102,11 +93,6 @@ namespace Ryujinx.Cpu.Jit
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
}
private void StepHandler(ExecutionContext context)
{
_exceptionCallbacks.StepCallback?.Invoke(this);
}
private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm)
{
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
@@ -123,16 +109,6 @@ namespace Ryujinx.Cpu.Jit
_impl.RequestInterrupt();
}
/// <inheritdoc/>
public void RequestDebugStep() => _impl.RequestDebugStep();
/// <inheritdoc/>
public ulong DebugPc
{
get => _impl.DebugPc;
set => _impl.DebugPc = value;
}
/// <inheritdoc/>
public void StopRunning()
{

View File

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

View File

@@ -81,8 +81,16 @@ namespace Ryujinx.Graphics.Device
if (index < Size)
{
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;
@@ -97,9 +105,9 @@ namespace Ryujinx.Graphics.Device
uint alignedOffset = index * RegisterSize;
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;
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
{
@@ -152,24 +162,5 @@ namespace Ryujinx.Graphics.Device
{
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);
Array8<Boolean32> enable = new();
Span<Boolean32> enableSpan = enable.AsSpan();
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>
@@ -237,14 +236,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private void UpdateColorMasks(IDeviceState state, int arg0)
{
Array8<RtColorMask> masks = new();
Span<RtColorMask> masksSpan = masks.AsSpan();
int index = 0;
for (int i = 0; i < 4; i++)
{
masksSpan[index++] = new RtColorMask((uint)arg0 & 0x1fff);
masksSpan[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
if (i != 3)
{
@@ -252,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
_processor.ThreedClass.UpdateColorMasks(masksSpan);
_processor.ThreedClass.UpdateColorMasks(ref masks);
}
/// <summary>

View File

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

View File

@@ -1,7 +1,6 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices;
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>
public void SetVertexStride(int index, int stride, int componentCount)
{
Span<Vector4<int>> vertexStridesSpan = _data.VertexStrides.AsSpan();
if (vertexStridesSpan[index].X != stride)
if (_data.VertexStrides[index].X != stride)
{
vertexStridesSpan[index].X = stride;
_data.VertexStrides[index].X = stride;
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;
ref int currentValue = ref GetElementRef(ref vertexStridesSpan[index], c);
ref int currentValue = ref GetElementRef(ref _data.VertexStrides[index], c);
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>
public void SetVertexOffset(int index, int offset, int divisor)
{
Span<Vector4<int>> vertexOffsetsSpan = _data.VertexOffsets.AsSpan();
if (vertexOffsetsSpan[index].X != offset)
if (_data.VertexOffsets[index].X != offset)
{
vertexOffsetsSpan[index].X = offset;
_data.VertexOffsets[index].X = offset;
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));
}
}

View File

@@ -125,14 +125,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
_vacContext.VertexInfoBufferUpdater.SetVertexCounts(_count, _instanceCount, _firstVertex, _firstInstance);
_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++)
{
VertexAttribState vertexAttrib = vertexAttribStateSpan[index];
VertexAttribState vertexAttrib = _state.State.VertexAttribState[index];
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();
GpuVa endAddress = vertexBufferEndAddressSpan[bufferIndex];
VertexBufferState vertexBuffer = vertexBufferStateSpan[bufferIndex];
bool instanced = vertexBufferInstancedSpan[bufferIndex];
GpuVa endAddress = _state.State.VertexBufferEndAddress[bufferIndex];
VertexBufferState vertexBuffer = _state.State.VertexBufferState[bufferIndex];
bool instanced = _state.State.VertexBufferInstanced[bufferIndex];
ulong address = vertexBuffer.Address.Pack();

View File

@@ -821,8 +821,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// on the screen scissor state, then we need to force only one texture to be bound to avoid
// host clipping.
ScreenScissorState screenScissorState = _state.State.ScreenScissorState;
Span<ScissorState> scissorStateSpan = _state.State.ScissorState.AsSpan();
bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 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;
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 &&
scissorState.Y1 == screenScissorState.Y &&
@@ -894,9 +892,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int scissorW = screenScissorState.Width;
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);
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.Shader;
using Ryujinx.Graphics.Shader;
using System;
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.
/// </summary>
/// <param name="state">The new state</param>
public void SetAttributeTypes(ReadOnlySpan<VertexAttribState> state)
public void SetAttributeTypes(ref Array32<VertexAttribState> state)
{
bool changed = false;
// ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
Span<AttributeType> attributeTypesSpan = _graphics.AttributeTypes.AsSpan();
ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
bool mayConvertVtgToCompute = ShaderCache.MayConvertVtgToCompute(ref _context.Capabilities);
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;
}
}
@@ -281,12 +279,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
/// <param name="rtControl">The render target control register</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;
int count = rtControl.UnpackCount();
Span<AttributeType> fragmentOutputTypesSpan = _graphics.FragmentOutputTypes.AsSpan();
for (int index = 0; index < Constants.TotalRenderTargets; index++)
{
@@ -300,9 +296,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
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;
}
}

View File

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

View File

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

View File

@@ -242,22 +242,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="rhs">Second struct</param>
/// <returns>True if equal, false otherwise</returns>
[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)
{
return Vector256.EqualsAll(
Unsafe.As<Span<T>, ReadOnlySpan<Vector256<uint>>>(ref lhs)[0],
Unsafe.As<Span<T>, ReadOnlySpan<Vector256<uint>>>(ref rhs)[0]
Unsafe.As<T, Vector256<uint>>(ref lhs),
Unsafe.As<T, Vector256<uint>>(ref rhs)
);
}
else
{
ReadOnlySpan<Vector128<uint>> lhsVec = Unsafe.As<Span<T>, ReadOnlySpan<Vector128<uint>>>(ref lhs);
ReadOnlySpan<Vector128<uint>> rhsVec = Unsafe.As<Span<T>, ReadOnlySpan<Vector128<uint>>>(ref rhs);
ref Vector128<uint> lhsVec = ref Unsafe.As<T, Vector128<uint>>(ref lhs);
ref Vector128<uint> rhsVec = ref Unsafe.As<T, Vector128<uint>>(ref rhs);
return Vector128.EqualsAll(lhsVec[0], rhsVec[0]) &&
Vector128.EqualsAll(lhsVec[1], rhsVec[1]);
return Vector128.EqualsAll(lhsVec, rhsVec) &&
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.
/// </summary>
/// <param name="masks">Blend enable</param>
public void UpdateBlendEnable(Span<Boolean32> enable)
public void UpdateBlendEnable(ref Array8<Boolean32> enable)
{
SetMmeShadowRamControlMode shadow = ShadowMode;
Span<Boolean32> state = _state.State.BlendEnable.AsSpan();
ref Array8<Boolean32> state = ref _state.State.BlendEnable;
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);
}
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.
/// </summary>
/// <param name="masks">Color masks</param>
public void UpdateColorMasks(Span<RtColorMask> masks)
public void UpdateColorMasks(ref Array8<RtColorMask> masks)
{
SetMmeShadowRamControlMode shadow = ShadowMode;
Span<RtColorMask> state = _state.State.RtColorMask.AsSpan();
ref Array8<RtColorMask> state = ref _state.State.RtColorMask;
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);
}
if (shadow.IsTrack())
{
masks.CopyTo(state);
_state.ShadowState.RtColorMask = masks;
}
}

View File

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

View File

@@ -1,5 +1,4 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
@@ -57,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="parent">Parent buffer</param>
/// <param name="stage">Initial buffer stage</param>
/// <param name="baseBuffers">Buffers to inherit state from</param>
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, RangeItem<Buffer>[] baseBuffers)
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, IEnumerable<Buffer> baseBuffers = null)
{
_size = (int)parent.Size;
_systemMemoryType = context.Capabilities.MemoryType;
@@ -73,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferStage storageFlags = stage & BufferStage.StorageMask;
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers.Length == 0)
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers == null)
{
_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.
}
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
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.
/// Must lock for any access from other threads.
/// </remarks>
private readonly NonOverlappingRangeList<Buffer> _buffers;
private readonly RangeList<Buffer> _buffers;
private readonly MultiRangeList<MultiRangeBuffer> _multiRangeBuffers;
private Buffer[] _bufferOverlaps;
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
private bool _pruneCaches;
@@ -61,6 +64,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_buffers = [];
_multiRangeBuffers = [];
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
_dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
// 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>
public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
{
Buffer[] overlaps = new Buffer[10];
int overlapCount;
MultiRange range = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
for (int index = 0; index < range.Count; index++)
{
MemoryRange subRange = range.GetSubRange(index);
_buffers.Lock.EnterReadLock();
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
for (int i = 0; i < overlaps.Length; i++)
lock (_buffers)
{
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>
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);
}
@@ -328,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
ulong alignedSize = alignedEndAddress - alignedAddress;
Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize).Value;
Buffer buffer = _buffers.FindFirstOverlap(alignedAddress, alignedSize);
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
@@ -395,7 +403,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
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);
physicalBuffers.Add(buffer);
@@ -487,12 +495,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param>
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
{
Buffer newBuffer = null;
_buffers.Lock.EnterWriteLock();
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
Buffer[] overlaps = _bufferOverlaps;
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
if (overlaps.Length != 0)
if (overlapsCount != 0)
{
// The buffer already exists. We can just return the existing 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.
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;
@@ -515,60 +522,53 @@ namespace Ryujinx.Graphics.Gpu.Memory
// sequential memory.
// 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.
if (overlaps.Length == 1 &&
address >= overlaps[0].Address &&
endAddress - overlaps[0].EndAddress <= BufferAlignmentSize * 2)
if (overlapsCount == 1 &&
address >= overlap0.Address &&
endAddress - overlap0.EndAddress <= BufferAlignmentSize * 2)
{
// Try to grow the buffer by 1.5x of its current size.
// This improves performance in the cases where the buffer is resized often by small amounts.
ulong existingSize = overlaps[0].Value.Size;
ulong existingSize = overlap0.Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize);
endAddress = address + size;
overlaps = _buffers.FindOverlapsAsSpan(address, size);
}
address = Math.Min(address, overlaps[0].Address);
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
for (int i = 0; i < overlaps.Length; i++)
{
anySparseCompatible |= overlaps[i].Value.SparseCompatible;
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
}
for (int index = 0; index < overlapsCount; index++)
{
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;
newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray);
}
else
{
_buffers.Lock.ExitWriteLock();
CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlaps, overlapsCount);
}
}
else
{
_buffers.Lock.ExitWriteLock();
// 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);
lock (_buffers)
{
_buffers.Add(buffer);
}
}
if (newBuffer is not null)
{
_buffers.Lock.EnterWriteLock();
_buffers.Add(newBuffer);
_buffers.Lock.ExitWriteLock();
}
ShrinkOverlapsBufferIfNeeded();
}
/// <summary>
@@ -582,75 +582,72 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="alignment">Alignment of the start address of the buffer</param>
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;
Buffer newBuffer = null;
_buffers.Lock.EnterWriteLock();
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
if (overlaps.Length != 0)
if (overlapsCount != 0)
{
// If the buffer already exists, make sure if covers the entire range,
// and make sure it is properly aligned, otherwise sparse mapping may fail.
ulong endAddress = address + size;
Buffer overlap0 = overlaps[0];
if (overlaps[0].Address > address ||
overlaps[0].EndAddress < endAddress ||
(overlaps[0].Address & (alignment - 1)) != 0 ||
(!overlaps[0].Value.SparseCompatible && sparseAligned))
if (overlap0.Address > address ||
overlap0.EndAddress < endAddress ||
(overlap0.Address & (alignment - 1)) != 0 ||
(!overlap0.SparseCompatible && sparseAligned))
{
// We need to make sure the new buffer is properly aligned.
// However, after the range is aligned, it is possible that it
// overlaps more buffers, so try again after each extension
// and ensure we cover all overlaps.
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
int oldOverlapCount;
int oldOverlapsCount;
do
{
address = Math.Min(address, overlaps[0].Address);
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = overlaps[index];
address = Math.Min(address, buffer.Address);
endAddress = Math.Max(endAddress, buffer.EndAddress);
}
address &= ~(alignment - 1);
oldOverlapCount = overlaps.Length;
overlaps = _buffers.FindOverlapsAsSpan(address, endAddress - address);
oldOverlapsCount = overlapsCount;
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;
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
_buffers.Lock.ExitWriteLock();
newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray);
}
else
{
_buffers.Lock.ExitWriteLock();
CreateBufferAligned(address, newSize, stage, sparseAligned, overlaps, overlapsCount);
}
}
else
{
_buffers.Lock.ExitWriteLock();
// No overlap, just create a new buffer.
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []);
Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseAligned);
lock (_buffers)
{
_buffers.Add(buffer);
}
}
if (newBuffer is not null)
{
_buffers.Lock.EnterWriteLock();
_buffers.Add(newBuffer);
_buffers.Lock.ExitWriteLock();
}
ShrinkOverlapsBufferIfNeeded();
}
/// <summary>
@@ -663,13 +660,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param>
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
/// <param name="overlaps">Buffers overlapping the range</param>
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, 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);
@@ -685,8 +688,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
NotifyBuffersModified?.Invoke();
RecreateMultiRangeBuffers(address, size);
return newBuffer;
}
/// <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>
/// Copy a buffer data from a given address to another.
/// </summary>
@@ -897,7 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
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);
@@ -945,7 +957,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (size != 0)
{
buffer = _buffers.FindOverlap(address, size).Value;
buffer = _buffers.FindFirstOverlap(address, size);
buffer.CopyFromDependantVirtualBuffers();
buffer.SynchronizeMemory(address, size);
@@ -957,7 +969,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
buffer = _buffers.FindOverlapFast(address, 1).Value;
buffer = _buffers.FindFirstOverlap(address, 1);
}
return buffer;
@@ -995,7 +1007,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (size != 0)
{
Buffer buffer = _buffers.FindOverlap(address, size).Value;
Buffer buffer = _buffers.FindFirstOverlap(address, size);
if (copyBackVirtual)
{

View File

@@ -258,7 +258,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
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));
@@ -282,7 +282,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
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));
@@ -761,7 +761,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!bounds.IsUnmapped)
{
bool isWrite = (bounds.Flags & BufferUsageFlags.Write) == BufferUsageFlags.Write;
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, bufferStage);
@@ -798,7 +798,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!bounds.IsUnmapped)
{
bool isWrite = (bounds.Flags & BufferUsageFlags.Write) == BufferUsageFlags.Write;
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
@@ -817,6 +817,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Bind respective buffer bindings on the host API.
/// </summary>
/// <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="isStorage">Indicates if the buffers are storage or uniform buffers</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -865,6 +866,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="texture">Buffer texture</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="format">Format of the buffer texture</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public void SetBufferTextureStorage(
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="bindingInfo">Binding info for the buffer texture</param>
/// <param name="index">Index of the binding on the array</param>
/// <param name="format">Format of the buffer texture</param>
public void SetBufferTextureStorage(
ShaderStage stage,
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="bindingInfo">Binding info for the buffer texture</param>
/// <param name="index">Index of the binding on the array</param>
/// <param name="format">Format of the buffer texture</param>
public void SetBufferTextureStorage(
ShaderStage stage,
IImageArray array,

View File

@@ -1,24 +1,25 @@
using Ryujinx.Common.Pools;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// A range within a buffer that has been modified by the GPU.
/// </summary>
class BufferModifiedRange : INonOverlappingRange
class BufferModifiedRange : IRange
{
/// <summary>
/// Start address of the range in guest memory.
/// </summary>
public ulong Address { get; internal set; }
public ulong Address { get; }
/// <summary>
/// Size of the range in bytes.
/// </summary>
public ulong Size { get; internal set; }
public ulong Size { get; }
/// <summary>
/// End address of the range in guest memory.
@@ -60,17 +61,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
return Address < address + size && address < EndAddress;
}
public INonOverlappingRange Split(ulong splitAddress)
{
throw new NotImplementedException();
}
}
/// <summary>
/// A structure used to track GPU modified ranges within a buffer.
/// </summary>
class BufferModifiedRangeList : NonOverlappingRangeList<BufferModifiedRange>
class BufferModifiedRangeList : RangeList<BufferModifiedRange>
{
private const int BackingInitialSize = 8;
@@ -81,6 +77,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private BufferMigration _source;
private BufferModifiedRangeList _migrationTarget;
private readonly Lock _lock = new();
/// <summary>
/// Whether the modified range list has any entries or not.
/// </summary>
@@ -88,10 +86,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
get
{
Lock.EnterReadLock();
bool result = Count > 0;
Lock.ExitReadLock();
return result;
lock (_lock)
{
return Count > 0;
}
}
}
@@ -116,32 +114,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <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)
{
// Slices a given region using the modified regions in the list. Calls the action for the new slices.
Lock.EnterReadLock();
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
for (int i = 0; i < overlaps.Length; i++)
lock (_lock)
{
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();
if (overlap.Address > address)
int count = FindOverlapsNonOverlapping(address, size, ref overlaps);
for (int i = 0; i < count; i++)
{
// The start of the remaining region is uncovered by this overlap. Call the action for it.
action(address, overlap.Address - address);
BufferModifiedRange overlap = overlaps[i];
if (overlap.Address > address)
{
// The start of the remaining region is uncovered by this overlap. Call the action for it.
action(address, overlap.Address - address);
}
// Remaining region is after this overlap.
size -= overlap.EndAddress - address;
address = overlap.EndAddress;
}
// Remaining region is after this overlap.
size -= overlap.EndAddress - address;
address = overlap.EndAddress;
}
Lock.ExitReadLock();
if ((long)size > 0)
{
// If there is any region left after removing the overlaps, signal it.
action(address, size);
if ((long)size > 0)
{
// If there is any region left after removing the overlaps, signal it.
action(address, size);
}
}
}
@@ -153,93 +152,51 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size of the modified region in bytes</param>
public void SignalModified(ulong address, ulong size)
{
ulong endAddress = address + size;
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)
// Must lock, as this can affect flushes from the background thread.
lock (_lock)
{
Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock();
return;
}
// We may overlap with some existing modified regions. They must be cut into by the new entry.
ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
if (first == last)
{
if (first.Address == address && first.EndAddress == endAddress)
int count = FindOverlapsNonOverlapping(address, size, ref overlaps);
ulong endAddress = address + size;
ulong syncNumber = _context.SyncNumber;
for (int i = 0; i < count; i++)
{
first.Value.SyncNumber = syncNumber;
first.Value.Parent = this;
Lock.ExitWriteLock();
return;
}
// The overlaps must be removed or split.
if (first.Address < address)
{
first.Value.Size = address - first.Address;
Update(first);
BufferModifiedRange overlap = overlaps[i];
if (first.EndAddress > endAddress)
if (overlap.Address == address && overlap.Size == size)
{
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.Value.SyncNumber, first.Value.Parent));
// Region already exists. Just update the existing sync number.
overlap.SyncNumber = syncNumber;
overlap.Parent = this;
return;
}
}
else
{
if (first.EndAddress > endAddress)
Remove(overlap);
if (overlap.Address < address && overlap.EndAddress > address)
{
first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress;
Update(first);
// A split item must be created behind this overlap.
Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent));
}
else
if (overlap.Address < endAddress && overlap.EndAddress > endAddress)
{
Remove(first.Value);
// A split item must be created after this overlap.
Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent));
}
}
Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock();
return;
}
BufferModifiedRange buffPre = null;
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address)
{
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
first.Value.SyncNumber, first.Value.Parent);
extendsPre = true;
}
if (last.EndAddress > endAddress)
{
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
last.Value.SyncNumber, last.Value.Parent);
extendsPost = true;
}
RemoveRange(first, last);
if (extendsPre)
{
Add(buffPre);
}
if (extendsPost)
{
Add(buffPost);
}
Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock();
}
/// <summary>
@@ -251,20 +208,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <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)
{
Lock.EnterReadLock();
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
int count = 0;
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)
{
rangeAction(overlap.Address, overlap.Size);
}
}
Lock.ExitReadLock();
}
/// <summary>
@@ -275,14 +237,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="rangeAction">The action to call for each modified range</param>
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.
Lock.EnterReadLock();
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
Lock.ExitReadLock();
int count = 0;
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);
}
}
@@ -295,11 +262,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>True if a range exists in the specified region, false otherwise</returns>
public bool HasRange(ulong address, ulong size)
{
Lock.EnterReadLock();
RangeItem<BufferModifiedRange> first = FindOverlapFast(address, size);
bool result = first is not null;
Lock.ExitReadLock();
return result;
// Range list must be consistent for this operation.
lock (_lock)
{
return FindOverlapsNonOverlapping(address, size, ref ThreadStaticArray<BufferModifiedRange>.Get()) > 0;
}
}
/// <summary>
@@ -331,35 +298,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="address">The start address of the flush range</param>
/// <param name="endAddress">The end address of the flush range</param>
private void RemoveRangesAndFlush(
RangeItem<BufferModifiedRange>[] overlaps,
BufferModifiedRange[] overlaps,
int rangeCount,
long highestDiff,
ulong currentSync,
ulong address,
ulong endAddress)
{
if (_migrationTarget == null)
lock (_lock)
{
ulong waitSync = currentSync + (ulong)highestDiff;
for (int i = 0; i < rangeCount; i++)
if (_migrationTarget == null)
{
BufferModifiedRange overlap = overlaps[i].Value;
ulong waitSync = currentSync + (ulong)highestDiff;
long diff = (long)(overlap.SyncNumber - currentSync);
if (diff <= highestDiff)
for (int i = 0; i < rangeCount; i++)
{
ulong clampAddress = Math.Max(address, overlap.Address);
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
BufferModifiedRange overlap = overlaps[i];
ClearPart(overlap, clampAddress, clampEnd);
long diff = (long)(overlap.SyncNumber - currentSync);
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction);
if (diff <= highestDiff)
{
ulong clampAddress = Math.Max(address, overlap.Address);
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
ClearPart(overlap, clampAddress, clampEnd);
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction);
}
}
}
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.
@@ -383,24 +353,31 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong endAddress = address + size;
ulong currentSync = _context.SyncNumber;
int rangeCount = 0;
ref BufferModifiedRange[] overlaps = ref ThreadStaticArray<BufferModifiedRange>.Get();
// Range list must be consistent for this operation
if (_migrationTarget != null)
lock (_lock)
{
_migrationTarget!.WaitForAndFlushRanges(address, size);
if (_migrationTarget != null)
{
rangeCount = -1;
}
else
{
rangeCount = FindOverlapsNonOverlapping(address, size, ref overlaps);
}
}
if (rangeCount == -1)
{
_migrationTarget.WaitForAndFlushRanges(address, size);
return;
}
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)
else if (rangeCount == 0)
{
Lock.ExitWriteLock();
return;
}
@@ -411,7 +388,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++)
{
BufferModifiedRange overlap = overlaps[i].Value;
BufferModifiedRange overlap = overlaps[i];
long diff = (long)(overlap.SyncNumber - currentSync);
@@ -423,17 +400,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (highestDiff == long.MinValue)
{
Lock.ExitWriteLock();
return;
}
// Wait for the syncpoint.
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress);
Lock.ExitWriteLock();
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
}
/// <summary>
@@ -446,40 +419,42 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="registerRangeAction">The action to call for each modified range</param>
public void InheritRanges(BufferModifiedRangeList ranges, Action<ulong, ulong> registerRangeAction)
{
ranges.Lock.EnterReadLock();
BufferModifiedRange[] inheritRanges = ranges.ToArray();
ranges.Lock.ExitReadLock();
BufferModifiedRange[] inheritRanges;
// Copy over the migration from the previous range list
BufferMigration oldMigration = ranges._source;
BufferMigrationSpan span = new(ranges._parent, ranges._flushAction, oldMigration);
ranges._parent.IncrementReferenceCount();
if (_source == null)
lock (ranges._lock)
{
// Create a new migration.
_source = new BufferMigration([span], this, _context.SyncNumber);
inheritRanges = ranges.ToArray();
_context.RegisterBufferMigration(_source);
lock (_lock)
{
// Copy over the migration from the previous range list
BufferMigration oldMigration = ranges._source;
BufferMigrationSpan span = new(ranges._parent, ranges._flushAction, oldMigration);
ranges._parent.IncrementReferenceCount();
if (_source == null)
{
// Create a new migration.
_source = new BufferMigration([span], this, _context.SyncNumber);
_context.RegisterBufferMigration(_source);
}
else
{
// Extend the migration
_source.AddSpanToEnd(span);
}
ranges._migrationTarget = this;
foreach (BufferModifiedRange range in inheritRanges)
{
Add(range);
}
}
}
else
{
// Extend the migration
_source.AddSpanToEnd(span);
}
ranges._migrationTarget = this;
Lock.EnterWriteLock();
foreach (BufferModifiedRange range in inheritRanges)
{
Add(range);
}
Lock.ExitWriteLock();
ulong currentSync = _context.SyncNumber;
foreach (BufferModifiedRange range in inheritRanges)
@@ -498,18 +473,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public void SelfMigration()
{
BufferMigrationSpan span = new(_parent, _parent.GetSnapshotDisposeAction(),
_parent.GetSnapshotFlushAction(), _source);
BufferMigration migration = new([span], this, _context.SyncNumber);
lock (_lock)
{
BufferMigrationSpan span = new(_parent, _parent.GetSnapshotDisposeAction(), _parent.GetSnapshotFlushAction(), _source);
BufferMigration migration = new([span], this, _context.SyncNumber);
// Migration target is used to redirect flush actions to the latest range list,
// so we don't need to set it here. (this range list is still the latest)
// Migration target is used to redirect flush actions to the latest range list,
// so we don't need to set it here. (this range list is still the latest)
_context.RegisterBufferMigration(migration);
_context.RegisterBufferMigration(migration);
Lock.EnterWriteLock();
_source = migration;
Lock.ExitWriteLock();
_source = migration;
}
}
/// <summary>
@@ -518,13 +493,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="migration">The migration to remove</param>
public void RemoveMigration(BufferMigration migration)
{
Lock.EnterWriteLock();
if (_source == migration)
lock (_lock)
{
_source = null;
if (_source == migration)
{
_source = null;
}
}
Lock.ExitWriteLock();
}
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>
public void Clear(ulong address, ulong size)
{
ulong endAddress = address + size;
Lock.EnterWriteLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null)
lock (_lock)
{
Lock.ExitWriteLock();
return;
}
// This function can be called from any thread, so it cannot use the arrays for background or foreground.
BufferModifiedRange[] toClear = new BufferModifiedRange[1];
if (first == last)
{
if (first.Address < address)
int rangeCount = FindOverlapsNonOverlapping(address, size, ref toClear);
ulong endAddress = address + size;
for (int i = 0; i < rangeCount; i++)
{
first.Value.Size = address - first.Address;
Update(first);
BufferModifiedRange overlap = toClear[i];
if (first.EndAddress > endAddress)
{
Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.Value.SyncNumber, first.Value.Parent));
}
ClearPart(overlap, address, endAddress);
}
else
{
if (first.EndAddress > endAddress)
{
first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress;
Update(first);
}
else
{
Remove(first.Value);
}
}
Lock.ExitWriteLock();
return;
}
}
BufferModifiedRange buffPre = null;
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address)
/// <summary>
/// Clear all modified ranges.
/// </summary>
public void Clear()
{
lock (_lock)
{
buffPre = new BufferModifiedRange(first.Address, address - first.Address,
first.Value.SyncNumber, first.Value.Parent);
extendsPre = true;
Count = 0;
}
if (last.EndAddress > endAddress)
{
buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress,
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 System;
using System.Runtime.CompilerServices;
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.
/// Must match ShaderStage for the shader stages, though anything after that can be in any order.
/// </summary>
[Flags]
internal enum BufferStage : byte
{
Compute,
@@ -54,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStage FromUsage(BufferUsageFlags flags)
{
if ((flags & BufferUsageFlags.Write) == BufferUsageFlags.Write)
if (flags.HasFlag(BufferUsageFlags.Write))
{
return BufferStage.StorageWrite;
}
@@ -67,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStage FromUsage(TextureUsageFlags flags)
{
if ((flags & TextureUsageFlags.ImageStore) == TextureUsageFlags.ImageStore)
if (flags.HasFlag(TextureUsageFlags.ImageStore))
{
return BufferStage.StorageWrite;
}

View File

@@ -690,8 +690,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_pageTable[l0] == null)
{
_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;

View File

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

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Represents a GPU virtual memory range.
/// </summary>
private class VirtualRange : INonOverlappingRange
private readonly struct VirtualRange : IRange
{
/// <summary>
/// GPU virtual address where the range starts.
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Size of the range in bytes.
/// </summary>
public ulong Size { get; private set; }
public ulong Size { get; }
/// <summary>
/// GPU virtual address where the range ends.
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Physical regions where the GPU virtual region is mapped.
/// </summary>
public MultiRange Range { get; private set; }
public MultiRange Range { get; }
/// <summary>
/// Creates a new virtual memory range.
@@ -60,14 +60,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
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 int _hasDeferredUnmaps;
@@ -79,6 +75,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_memoryManager = memoryManager;
_virtualRanges = [];
_virtualRangeOverlaps = new VirtualRange[BufferCache.OverlapsBufferInitialCapacity];
_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>
public bool TryGetOrAddRange(ulong gpuVa, ulong size, out MultiRange range)
{
VirtualRange[] overlaps = _virtualRangeOverlaps;
int overlapsCount;
if (Interlocked.Exchange(ref _hasDeferredUnmaps, 0) != 0)
{
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;
_virtualRanges.Lock.EnterWriteLock();
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
if (first is not null)
overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(gpuVa, size, ref overlaps);
if (overlapsCount != 0)
{
// 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.
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);
endAddress = Math.Max(endAddress, last.EndAddress);
_virtualRanges.RemoveRange(first, last);
for (int index = 0; index < overlapsCount; index++)
{
VirtualRange virtualRange = overlaps[index];
gpuVa = Math.Min(gpuVa, virtualRange.Address);
endAddress = Math.Max(endAddress, virtualRange.EndAddress);
_virtualRanges.Remove(virtualRange);
}
ulong newSize = endAddress - gpuVa;
MultiRange newRange = _memoryManager.GetPhysicalRegions(gpuVa, newSize);
@@ -147,8 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
found = first.Value.Range.Count == 1 || IsSparseAligned(first.Value.Range);
range = first.Value.Range.Slice(gpuVa - first.Address, size);
found = overlap0.Range.Count == 1 || IsSparseAligned(overlap0.Range);
range = overlap0.Range.Slice(gpuVa - overlap0.Address, size);
}
}
else
@@ -160,7 +170,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_virtualRanges.Add(virtualRange);
}
_virtualRanges.Lock.ExitWriteLock();
ShrinkOverlapsBufferIfNeeded();
// If the range is not properly aligned for sparse mapping,
// let's just force it to a single range.
@@ -210,5 +221,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
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.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Types;
@@ -259,25 +258,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
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++)
{
int rtIndex = rtControl.UnpackPermutationIndex(index);
var colorState = rtColorStateSpan[rtIndex];
var colorState = state.RtColorState[rtIndex];
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
{
attachmentEnableSpan[index] = false;
attachmentFormatsSpan[index] = Format.R8G8B8A8Unorm;
pipeline.AttachmentEnable[index] = false;
pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm;
}
else
{
attachmentEnableSpan[index] = true;
attachmentFormatsSpan[index] = colorState.Format.Convert().Format;
pipeline.AttachmentEnable[index] = true;
pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format;
}
}
@@ -585,18 +580,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
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++)
{
var tf = tfStateSpan[i];
var tf = state.TfState[i];
descs[i] = new TransformFeedbackDescriptor(
tf.BufferIndex,
tf.Stride,
tf.VaryingsCount,
ref tfVaryingLocationsSpan[i]);
ref state.TfVaryingLocations[i]);
}
return descs;

View File

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

View File

@@ -92,24 +92,20 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264
if (pictureInfo.ScalingMatrixPresent)
{
Span<Array16<byte>> scalingLists4x4Span = pictureInfo.ScalingLists4x4.AsSpan();
for (int index = 0; index < 6; index++)
{
writer.WriteBit(true);
WriteScalingList(ref writer, scalingLists4x4Span[index]);
WriteScalingList(ref writer, pictureInfo.ScalingLists4x4[index]);
}
if (pictureInfo.Transform8x8ModeFlag)
{
Span<Array64<byte>> scalingLists8x8Span = pictureInfo.ScalingLists8x8.AsSpan();
for (int index = 0; index < 2; index++)
{
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;
Span<byte> listSpan = list.AsSpan();
for (int index = 0; index < listSpan.Length; index++)
for (int index = 0; index < list.Length; index++)
{
byte value = listSpan[scan[index]];
byte value = list[scan[index]];
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)
{
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)
{
Span<byte> tx8x8ProbSpan2 = tx8x8ProbSpan1[i].AsSpan();
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)
{
Span<byte> tx16x16ProbSpan2 = tx16x16ProbSpan1[i].AsSpan();
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)
{
Span<byte> tx32x32ProbSpan2 = tx32x32ProbSpan1[i].AsSpan();
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)
{
Span<Array2<byte>> switchableInterpProbSpan1 = fc.SwitchableInterpProb.AsSpan();
for (int i = 0; i < Constants.SwitchableFilterContexts; ++i)
for (int j = 0; j < Constants.SwitchableFilterContexts; ++j)
{
Span<byte> switchableInterpProbSpan2 = switchableInterpProbSpan1[i].AsSpan();
for (int j = 0; j < Constants.SwitchableFilters - 1; ++j)
for (int i = 0; i < Constants.SwitchableFilters - 1; ++i)
{
r.DiffUpdateProb(ref switchableInterpProbSpan2[j]);
r.DiffUpdateProb(ref fc.SwitchableInterpProb[j][i]);
}
}
}
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)
{
Span<byte> interModeProbSpan2 = interModeProbSpan1[i].AsSpan();
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);
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)
{
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref signSpan[i], 1), 1);
r.UpdateMvProbs(classesSpan[i].AsSpan(), EntropyMv.Classes - 1);
r.UpdateMvProbs(class0Span[i].AsSpan(), EntropyMv.Class0Size - 1);
r.UpdateMvProbs(bitsSpan[i].AsSpan(), EntropyMv.OffsetBits);
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Sign[i], 1), 1);
r.UpdateMvProbs(ctx.Classes[i].AsSpan(), EntropyMv.Classes - 1);
r.UpdateMvProbs(ctx.Class0[i].AsSpan(), EntropyMv.Class0Size - 1);
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)
{
Span<Array3<byte>> class0FpSpan2 = class0FpSpan1[i].AsSpan();
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)
{
Span<byte> class0HpSpan = ctx.Class0Hp.AsSpan();
Span<byte> hpSpan = ctx.Hp.AsSpan();
for (int i = 0; i < 2; ++i)
{
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref class0HpSpan[i], 1), 1);
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref hpSpan[i], 1), 1);
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Class0Hp[i], 1), 1);
r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Hp[i], 1), 1);
}
}
}
@@ -782,16 +751,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int refr;
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)
{
int frame = refFrameSpan[refr];
ref RefBuffer refBuf = ref frameRefsSpan[frame - Constants.LastFrame];
int frame = mi.RefFrame[refr];
ref RefBuffer refBuf = ref cm.FrameRefs[frame - Constants.LastFrame];
ref ScaleFactors sf = ref refBuf.Sf;
ref Surface refFrameBuf = ref refBuf.Buf;
@@ -804,13 +767,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
isScaled = sf.IsScaled();
ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol,
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)
{
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;
int num4X4W = pd.N4W;
int num4X4H = pd.N4H;
@@ -848,10 +811,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
}
else
{
Mv mv = mvSpan[refr];
Mv mv = mi.Mv[refr];
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;
int num4X4W = pd.N4W;
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)
{
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; i++)
{
planeSpan[i].N4W = (ushort)((bw << 1) >> planeSpan[i].SubsamplingX);
planeSpan[i].N4H = (ushort)((bh << 1) >> planeSpan[i].SubsamplingY);
planeSpan[i].N4Wl = (byte)(bwl - planeSpan[i].SubsamplingX);
planeSpan[i].N4Hl = (byte)(bhl - planeSpan[i].SubsamplingY);
xd.Plane[i].N4W = (ushort)((bw << 1) >> xd.Plane[i].SubsamplingX);
xd.Plane[i].N4H = (ushort)((bh << 1) >> xd.Plane[i].SubsamplingY);
xd.Plane[i].N4Wl = (byte)(bwl - xd.Plane[i].SubsamplingX);
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
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;
}
@@ -972,11 +933,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (!mi.IsInterBlock())
{
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
for (int plane = 0; plane < Constants.MaxMbPlane; ++plane)
int 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;
int num4X4W = pd.N4W;
int num4X4H = pd.N4H;
@@ -1007,13 +967,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Reconstruction
if (mi.Skip == 0)
{
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
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;
int num4X4W = pd.N4W;
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)
{
if (r.ReadBit() != 0)
{
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)
{
Span<Array6<Array3<byte>>> coefProbs3 = coefProbs2[j].AsSpan();
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)
{
Span<byte> coefProbs5 = coefProbs4[l].AsSpan();
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)
{
Span<Array2<Array2<Array6<Array6<Array3<byte>>>>>> coefProbsSpan = fc.CoefProbs.AsSpan();
int maxTxSize = (int)Luts.TxModeToBiggestTxSize[(int)txMode];
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;
if (lf.ModeRefDeltaUpdate)
{
Span<sbyte> refDeltasSpan = lf.RefDeltas.AsSpan();
Span<sbyte> modeDeltasSpan = lf.ModeDeltas.AsSpan();
for (int i = 0; i < LoopFilter.MaxRefLfDeltas; i++)
{
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)
{
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);
SetupRenderSize(ref cm, ref rb);
Span<RefCntBuffer> frameBuffsSpan = pool.FrameBufs.AsSpan();
if (cm.GetFrameNewBuffer().ReallocFrameBuffer(
allocator,
cm.Width,
@@ -1333,21 +1276,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.UseHighBitDepth,
Surface.DecBorderInPixels,
cm.ByteAlignment,
new Ptr<VpxCodecFrameBuffer>(ref frameBuffsSpan[cm.NewFbIdx].RawFrameBuffer),
new Ptr<VpxCodecFrameBuffer>(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer),
FrameBuffers.GetFrameBuffer,
pool.CbPriv) != 0)
{
cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer");
}
frameBuffsSpan[cm.NewFbIdx].Released = 0;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY;
frameBuffsSpan[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight;
pool.FrameBufs[cm.NewFbIdx].Released = 0;
pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX;
pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY;
pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth;
pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace;
pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange;
pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth;
pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight;
}
private static bool ValidRefFrameImgFmt(
@@ -1368,15 +1311,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
bool hasValidRefFrame = false;
ref BufferPool pool = ref cm.BufferPool.Value;
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
for (int i = 0; i < Constants.RefsPerFrame; ++i)
{
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;
height = buf.YCropHeight;
found = true;
@@ -1401,7 +1342,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// has valid dimensions.
for (int i = 0; i < Constants.RefsPerFrame; ++i)
{
ref RefBuffer refFrame = ref frameRefsSpan[i];
ref RefBuffer refFrame = ref cm.FrameRefs[i];
hasValidRefFrame |=
refFrame.Idx != RefBuffer.InvalidIdx &&
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)
{
ref RefBuffer refFrame = ref frameRefsSpan[i];
ref RefBuffer refFrame = ref cm.FrameRefs[i];
if (refFrame.Idx == RefBuffer.InvalidIdx ||
!ValidRefFrameImgFmt(
(BitDepth)refFrame.Buf.BitDepth,
@@ -1432,8 +1373,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.ResizeContextBuffers(allocator, width, height);
SetupRenderSize(ref cm, ref rb);
Span<RefCntBuffer> frameBuffsSpan = pool.FrameBufs.AsSpan();
if (cm.GetFrameNewBuffer().ReallocFrameBuffer(
allocator,
@@ -1444,21 +1383,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.UseHighBitDepth,
Surface.DecBorderInPixels,
cm.ByteAlignment,
new Ptr<VpxCodecFrameBuffer>(ref frameBuffsSpan[cm.NewFbIdx].RawFrameBuffer),
new Ptr<VpxCodecFrameBuffer>(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer),
FrameBuffers.GetFrameBuffer,
pool.CbPriv) != 0)
{
cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer");
}
frameBuffsSpan[cm.NewFbIdx].Released = 0;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX;
frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY;
frameBuffsSpan[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace;
frameBuffsSpan[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth;
frameBuffsSpan[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight;
pool.FrameBufs[cm.NewFbIdx].Released = 0;
pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX;
pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY;
pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth;
pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace;
pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange;
pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth;
pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight;
}
// 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,
Span<TileBuffer> tileBuffers)
ref Array64<TileBuffer> tileBuffers)
{
for (int c = 0; c < tileCols; ++c)
{
@@ -1514,16 +1453,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ArrayPtr<byte> data,
int tileCols,
int tileRows,
Span<Array64<TileBuffer>> tileBuffers1)
ref Array4<Array64<TileBuffer>> tileBuffers)
{
for (int r = 0; r < tileRows; ++r)
{
Span<TileBuffer> tileBuffers2 = tileBuffers1[r].AsSpan();
for (int c = 0; c < tileCols; ++c)
{
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);
}
}
@@ -1547,20 +1484,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
MemoryUtil.Fill(cm.AboveSegContext.ToPointer(), (sbyte)0, alignedCols);
LoopFilter.ResetLfm(ref cm);
Span<Array64<TileBuffer>> tileBuffers1 = tileBuffers.AsSpan();
Span<TileWorkerData> tileWorkerDataSpan = cm.TileWorkerData.AsSpan();
GetTileBuffers(ref cm, data, tileCols, tileRows, tileBuffers1);
GetTileBuffers(ref cm, data, tileCols, tileRows, ref tileBuffers);
// Load all tile information into tile_data.
for (tileRow = 0; tileRow < tileRows; ++tileRow)
{
Span<TileBuffer> tileBuffers2 = tileBuffers1[tileRow].AsSpan();
for (tileCol = 0; tileCol < tileCols; ++tileCol)
{
ref TileBuffer buf = ref tileBuffers2[tileCol];
ref TileWorkerData tileData = ref tileWorkerDataSpan[(tileCols * tileRow) + tileCol];
ref TileBuffer buf = ref tileBuffers[tileRow][tileCol];
ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + tileCol];
tileData.Xd = cm.Mb;
tileData.Xd.Corrupted = false;
tileData.Xd.Counts = cm.Counts;
@@ -1580,7 +1512,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (tileCol = 0; tileCol < tileCols; ++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);
tileData.Xd.LeftContext = new Array3<Array16<sbyte>>();
tileData.Xd.LeftSegContext = new Array8<sbyte>();
@@ -1599,11 +1531,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
}
// 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,
Span<TileBuffer> tileBuffers)
ref Array64<TileBuffer> tileBuffers)
{
ref TileInfo tile = ref tileData.Xd.Tile;
int finalCol = (1 << cm.Log2TileCols) - 1;
@@ -1661,12 +1593,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.AboveContext.AsSpan().Clear();
cm.AboveSegContext.AsSpan().Clear();
Span<TileWorkerData> tileWorkerDataSpan = cm.TileWorkerData.AsSpan();
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.Counts = new Ptr<Vp9BackwardUpdates>(ref tileData.Counts);
@@ -1674,17 +1604,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
}
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)
{
TileBuffer largest = tileBuffersSpan[0];
tileBuffersSpan[1..].CopyTo(tileBuffersSpan[..^1]);
tileBuffersSpan[tileCols - 1] = largest;
TileBuffer largest = tileBuffers[0];
Span<TileBuffer> buffers = tileBuffers.AsSpan();
buffers[1..].CopyTo(buffers[..(tileBuffers.Length - 1)]);
tileBuffers[tileCols - 1] = largest;
}
else
{
@@ -1695,9 +1625,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// larger tile implies it is more difficult to decode.
while (start < end)
{
tmp = tileBuffersSpan[start];
tileBuffersSpan[start] = tileBuffersSpan[end];
tileBuffersSpan[end] = tmp;
tmp = tileBuffers[start];
tileBuffers[start] = tileBuffers[end];
tileBuffers[end] = tmp;
start += 2;
end -= 2;
}
@@ -1710,7 +1640,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (n = 0; n < numWorkers; ++n)
{
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.BufEnd = bufStart + count - 1;
@@ -1724,7 +1654,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
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;
}
@@ -1734,14 +1664,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
if (bitReaderEnd.IsNull)
{
ref TileWorkerData tileData = ref tileWorkerDataSpan[n - 1 + totalTiles];
ref TileWorkerData tileData = ref cm.TileWorkerData[n - 1 + totalTiles];
bitReaderEnd = tileData.DataEnd;
}
}
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);
}
@@ -1775,7 +1705,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
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;
for (int i = 0; i < Constants.FrameBuffers; ++i)
@@ -1785,11 +1715,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
continue;
}
frameBuffs[i].RefCount = 0;
if (frameBuffs[i].Released == 0)
frameBufs[i].RefCount = 0;
if (frameBufs[i].Released == 0)
{
FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBuffs[i].RawFrameBuffer);
frameBuffs[i].Released = 1;
FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[i].RawFrameBuffer);
frameBufs[i].Released = 1;
}
}
}
@@ -1812,7 +1742,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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;
@@ -1831,15 +1761,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
ref Vp9Common cm = ref pbi.Common;
ref BufferPool pool = ref cm.BufferPool.Value;
Span<RefCntBuffer> frameBuffs = pool.FrameBufs.AsSpan();
ref Array12<RefCntBuffer> frameBufs = ref pool.FrameBufs;
int mask, refIndex = 0;
ulong sz;
cm.LastFrameType = cm.FrameType;
cm.LastIntraOnly = cm.IntraOnly;
Span<int> refFrameSpan = cm.RefFrameMap.AsSpan();
if (rb.ReadLiteral(2) != FrameMarker)
{
cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame marker");
@@ -1855,14 +1783,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (cm.ShowExistingFrame != 0)
{
// Show an existing frame directly.
int frameToShow = refFrameSpan[rb.ReadLiteral(3)];
if (frameToShow < 0 || frameBuffs[frameToShow].RefCount < 1)
int frameToShow = cm.RefFrameMap[rb.ReadLiteral(3)];
if (frameToShow < 0 || frameBufs[frameToShow].RefCount < 1)
{
cm.Error.InternalError(CodecErr.UnsupBitstream,
$"Buffer {frameToShow} does not contain a decoded frame");
}
RefCntFb(frameBuffs, ref cm.NewFbIdx, frameToShow);
RefCntFb(ref frameBufs, ref cm.NewFbIdx, frameToShow);
pbi.RefreshFrameFlags = 0;
cm.Lf.FilterLevel = 0;
cm.ShowFrame = 1;
@@ -1884,18 +1812,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
cm.ReadBitdepthColorspaceSampling(ref rb);
pbi.RefreshFrameFlags = (1 << Constants.RefFrames) - 1;
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
for (int i = 0; i < Constants.RefsPerFrame; ++i)
{
frameRefsSpan[i].Idx = RefBuffer.InvalidIdx;
frameRefsSpan[i].Buf = default;
cm.FrameRefs[i].Idx = RefBuffer.InvalidIdx;
cm.FrameRefs[i].Buf = default;
}
SetupFrameSize(allocator, ref cm, ref rb);
if (pbi.NeedResync != 0)
{
refFrameSpan.Fill(-1);
cm.RefFrameMap.AsSpan().Fill(-1);
FlushAllFbOnKey(ref cm);
pbi.NeedResync = 0;
}
@@ -1934,25 +1860,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
SetupFrameSize(allocator, ref cm, ref rb);
if (pbi.NeedResync != 0)
{
refFrameSpan.Fill(-1);
cm.RefFrameMap.AsSpan().Fill(-1);
pbi.NeedResync = 0;
}
}
else if (pbi.NeedResync != 1)
{
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
Span<sbyte> refFrameSignBiasSpan = cm.RefFrameSignBias.AsSpan();
/* Skip if need resync */
pbi.RefreshFrameFlags = rb.ReadLiteral(Constants.RefFrames);
for (int i = 0; i < Constants.RefsPerFrame; ++i)
{
int refr = rb.ReadLiteral(Constants.RefFramesLog2);
int idx = refFrameSpan[refr];
ref RefBuffer refFrame = ref frameRefsSpan[i];
int idx = cm.RefFrameMap[refr];
ref RefBuffer refFrame = ref cm.FrameRefs[i];
refFrame.Idx = idx;
refFrame.Buf = frameBuffs[idx].Buf;
refFrameSignBiasSpan[Constants.LastFrame + i] = (sbyte)rb.ReadBit();
refFrame.Buf = frameBufs[idx].Buf;
cm.RefFrameSignBias[Constants.LastFrame + i] = (sbyte)rb.ReadBit();
}
SetupFrameSizeWithRefs(allocator, ref cm, ref rb);
@@ -1962,7 +1885,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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.Buf.YCropWidth,
refBuf.Buf.YCropHeight,
@@ -2003,25 +1926,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// below, forcing the use of context 0 for those frame types.
cm.FrameContextIdx = (uint)rb.ReadLiteral(Constants.FrameContextsLog2);
Span<int> nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan();
// Generate next_ref_frame_map.
for (mask = pbi.RefreshFrameFlags; mask != 0; mask >>= 1)
{
if ((mask & 1) != 0)
{
nextRefFrameMapSpan[refIndex] = cm.NewFbIdx;
++frameBuffs[cm.NewFbIdx].RefCount;
cm.NextRefFrameMap[refIndex] = cm.NewFbIdx;
++frameBufs[cm.NewFbIdx].RefCount;
}
else
{
nextRefFrameMapSpan[refIndex] = cm.RefFrameMap[refIndex];
cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex];
}
// Current thread holds the reference frame.
if (cm.RefFrameMap[refIndex] >= 0)
{
++frameBuffs[cm.RefFrameMap[refIndex]].RefCount;
++frameBufs[cm.RefFrameMap[refIndex]].RefCount;
}
++refIndex;
@@ -2029,11 +1950,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (; refIndex < Constants.RefFrames; ++refIndex)
{
nextRefFrameMapSpan[refIndex] = refFrameSpan[refIndex];
cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex];
// 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);
Span<byte> skipProbSpan = fc.SkipProb.AsSpan();
for (int k = 0; k < Constants.SkipContexts; ++k)
{
r.DiffUpdateProb(ref skipProbSpan[k]);
r.DiffUpdateProb(ref fc.SkipProb[k]);
}
if (!cm.FrameIsIntraOnly())
@@ -2095,12 +2014,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
ReadSwitchableInterpProbs(ref fc, ref r);
}
Span<byte> intraInterProbSpan = fc.IntraInterProb.AsSpan();
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);
@@ -2110,28 +2027,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
}
cm.ReadFrameReferenceModeProbs(ref r);
Span<Array9<byte>> yModeProbSpan1 = fc.YModeProb.AsSpan();
for (int j = 0; j < EntropyMode.BlockSizeGroups; j++)
{
Span<byte> yModeProbSpan2 = yModeProbSpan1[j].AsSpan();
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)
{
Span<byte> partitionProbSpan2 = partitionProbSpan1[j].AsSpan();
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);
Span<Vp9EntropyProbs> frameContextsSpan = cm.FrameContexts.AsSpan();
cm.Fc = new Ptr<Vp9EntropyProbs>(ref frameContextsSpan[(int)cm.FrameContextIdx]);
cm.Fc = new Ptr<Vp9EntropyProbs>(ref cm.FrameContexts[(int)cm.FrameContextIdx]);
xd.Corrupted = false;
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.
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;
}
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)
@@ -187,7 +187,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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);
return segmentId;
}
@@ -223,15 +223,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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);
segmentId = mi.SegIdPredicted != 0
? predictedSegmentId
: ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan());
: ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
}
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);
@@ -272,12 +272,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits
Span<byte> bitsSpan = fc.Bits[mvcomp].AsSpan();
d = 0;
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);
@@ -345,7 +343,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref MacroBlockD xd,
ref Reader r,
int segmentId,
Span<sbyte> refFrame)
ref Array2<sbyte> refFrame)
{
ref Vp9EntropyProbs fc = ref cm.Fc.Value;
@@ -420,25 +418,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
BlockSize bsize = mi.SbType;
Span<BModeInfo> bmiSpan = mi.Bmi.AsSpan();
switch (bsize)
{
case BlockSize.Block4X4:
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;
case BlockSize.Block4X8:
bmiSpan[0].Mode = bmiSpan[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[0].Mode = mi.Bmi[2].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;
case BlockSize.Block8X4:
bmiSpan[0].Mode = bmiSpan[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[0].Mode = mi.Bmi[1].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;
default:
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()
mi.InterpFilter = Constants.SwitchableFilters;
Span<sbyte> refFrameSpan = mi.RefFrame.AsSpan();
refFrameSpan[0] = Constants.IntraFrame;
refFrameSpan[1] = Constants.None;
mi.RefFrame[0] = Constants.IntraFrame;
mi.RefFrame[1] = Constants.None;
}
private static void CopyPair(ref Array2<Mv> dst, ref Array2<Mv> src)
{
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(
ref Vp9Common cm,
ref MacroBlockD xd,
PredictionMode mode,
Span<Mv> mv,
Span<Mv> refMv,
Span<Mv> nearNearestMv,
ref Array2<Mv> mv,
ref Array2<Mv> refMv,
ref Array2<Mv> nearNearestMv,
int isCompound,
bool allowHp,
ref Reader r)
@@ -485,12 +491,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
case PredictionMode.NearMv:
case PredictionMode.NearestMv:
{
nearNearestMv.CopyTo(mv);
CopyPair(ref mv, ref nearNearestMv);
break;
}
case PredictionMode.ZeroMv:
{
mv.Clear();
ZeroPair(ref mv);
break;
}
default:
@@ -553,29 +559,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
private static bool IsDiffRefFrameAddEb(
ref ModeInfo mbmi,
sbyte refFrame,
Span<sbyte> refSignBias,
ref Array4<sbyte> refSignBias,
ref int refmvCount,
Span<Mv> mvRefList,
bool earlyBreak)
{
if (mbmi.IsInterBlock())
{
Span<sbyte> refFrameSpan = mbmi.RefFrame.AsSpan();
Span<Mv> mvSpan = mbmi.Mv.AsSpan();
if (refFrameSpan[0] != refFrame)
if (mbmi.RefFrame[0] != refFrame)
{
if (AddRefListEb(mbmi.ScaleMv(0, refFrame, refSignBias), ref refmvCount, mvRefList,
if (AddRefListEb(mbmi.ScaleMv(0, refFrame, ref refSignBias), ref refmvCount, mvRefList,
earlyBreak))
{
return true;
}
}
if (mbmi.HasSecondRef() && refFrameSpan[1] != refFrame &&
Unsafe.As<Mv, int>(ref mvSpan[1]) != Unsafe.As<Mv, int>(ref mvSpan[0]))
if (mbmi.HasSecondRef() && mbmi.RefFrame[1] != refFrame &&
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))
{
return true;
@@ -600,7 +603,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int block,
int isSub8X8)
{
Span<sbyte> refSignBias = cm.RefFrameSignBias.AsSpan();
ref Array4<sbyte> refSignBias = ref cm.RefFrameSignBias;
int i, refmvCount = 0;
bool differentRefFound = false;
Ptr<MvRef> prevFrameMvs = cm.UsePrevFrameMvs
@@ -613,7 +616,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Blank the reference vector list
mvRefList[..Constants.MaxMvRefCandidates].Clear();
i = 0;
if (isSub8X8 != 0)
{
@@ -627,9 +630,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value;
differentRefFound = true;
Span<sbyte> refFrameSpan = candidateMi.RefFrame.AsSpan();
if (refFrameSpan[0] == refFrame)
if (candidateMi.RefFrame[0] == refFrame)
{
if (AddRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount,
mvRefList, earlyBreak))
@@ -637,7 +638,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
goto Done;
}
}
else if (refFrameSpan[1] == refFrame)
else if (candidateMi.RefFrame[1] == refFrame)
{
if (AddRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount,
mvRefList, earlyBreak))
@@ -659,20 +660,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value;
differentRefFound = true;
Span<sbyte> refFrameSpan = candidate.RefFrame.AsSpan();
Span<Mv> mvSpan = candidate.Mv.AsSpan();
if (refFrameSpan[0] == refFrame)
if (candidate.RefFrame[0] == refFrame)
{
if (AddRefListEb(mvSpan[0], ref refmvCount, mvRefList, earlyBreak))
if (AddRefListEb(candidate.Mv[0], ref refmvCount, mvRefList, earlyBreak))
{
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;
}
@@ -683,19 +681,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Check the last frame's mode and mv info.
if (!prevFrameMvs.IsNull)
{
Span<sbyte> refFrameSpan = prevFrameMvs.Value.RefFrame.AsSpan();
Span<Mv> mvSpan = prevFrameMvs.Value.Mv.AsSpan();
if (refFrameSpan[0] == refFrame)
if (prevFrameMvs.Value.RefFrame[0] == refFrame)
{
if (AddRefListEb(mvSpan[0], ref refmvCount, mvRefList, earlyBreak))
if (AddRefListEb(prevFrameMvs.Value.Mv[0], ref refmvCount, mvRefList, earlyBreak))
{
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;
}
@@ -715,7 +710,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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 (IsDiffRefFrameAddEb(ref candidate, refFrame, refSignBias, ref refmvCount, mvRefList,
if (IsDiffRefFrameAddEb(ref candidate, refFrame, ref refSignBias, ref refmvCount, mvRefList,
earlyBreak))
{
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.
if (!prevFrameMvs.IsNull)
{
Span<sbyte> refFrameSpan = prevFrameMvs.Value.RefFrame.AsSpan();
Span<Mv> mvSpan = prevFrameMvs.Value.Mv.AsSpan();
if (refFrameSpan[0] != refFrame && refFrameSpan[0] > Constants.IntraFrame)
if (prevFrameMvs.Value.RefFrame[0] != refFrame && prevFrameMvs.Value.RefFrame[0] > Constants.IntraFrame)
{
Mv mv = mvSpan[0];
if (refSignBias[refFrameSpan[0]] != refSignBias[refFrame])
Mv mv = prevFrameMvs.Value.Mv[0];
if (refSignBias[prevFrameMvs.Value.RefFrame[0]] != refSignBias[refFrame])
{
mv.Row *= -1;
mv.Col *= -1;
@@ -745,13 +737,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
}
}
if (refFrameSpan[1] > Constants.IntraFrame &&
refFrameSpan[1] != refFrame &&
Unsafe.As<Mv, int>(ref mvSpan[1]) !=
Unsafe.As<Mv, int>(ref mvSpan[0]))
if (prevFrameMvs.Value.RefFrame[1] > Constants.IntraFrame &&
prevFrameMvs.Value.RefFrame[1] != refFrame &&
Unsafe.As<Mv, int>(ref prevFrameMvs.Value.Mv[1]) !=
Unsafe.As<Mv, int>(ref prevFrameMvs.Value.Mv[0]))
{
Mv mv = mvSpan[1];
if (refSignBias[refFrameSpan[1]] != refSignBias[refFrame])
Mv mv = prevFrameMvs.Value.Mv[1];
if (refSignBias[prevFrameMvs.Value.RefFrame[1]] != refSignBias[refFrame])
{
mv.Row *= -1;
mv.Col *= -1;
@@ -797,7 +789,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
Span<Mv> mvList = stackalloc Mv[Constants.MaxMvRefCandidates];
ref ModeInfo mi = ref xd.Mi[0].Value;
Span<BModeInfo> bmi = mi.Bmi.AsSpan();
ref Array4<BModeInfo> bmi = ref mi.Bmi;
int refmvCount;
Debug.Assert(Constants.MaxMvRefCandidates == 2);
@@ -892,12 +884,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
BlockSize bsize = mi.SbType;
bool allowHp = cm.AllowHighPrecisionMv;
Array2<Mv> bestRefMvs = new();
Span<Mv> bestRefMvsSpan = bestRefMvs.AsSpan();
int refr, isCompound;
byte interModeCtx;
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;
interModeCtx = GetModeContext(ref cm, ref xd, mvRefSearch, miRow, miCol);
@@ -929,17 +920,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (mi.Mode != PredictionMode.ZeroMv)
{
Span<Mv> tmpMvs = stackalloc Mv[Constants.MaxMvRefCandidates];
Span<sbyte> refFrameSpan = mi.RefFrame.AsSpan();
for (refr = 0; refr < 1 + isCompound; ++refr)
{
sbyte frame = refFrameSpan[refr];
sbyte frame = mi.RefFrame[refr];
int refmvCount;
refmvCount = DecFindRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol,
-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;
PredictionMode bMode = 0;
Array2<Mv> bestSub8X8 = new();
Span<Mv> bestSub8X8Span = bestSub8X8.AsSpan();
Span<BModeInfo> bmiSpan = mi.Bmi.AsSpan();
const uint InvalidMv = 0x80008000;
// Initialize the 2nd element as even though it won't be used meaningfully
// 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 (idx = 0; idx < 2; idx += num4X4W)
@@ -973,11 +961,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (refr = 0; refr < 1 + isCompound; ++refr)
{
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))
{
xd.Corrupted |= true;
@@ -986,22 +974,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if (num4X4H == 2)
{
bmiSpan[j + 2] = bmiSpan[j];
mi.Bmi[j + 2] = mi.Bmi[j];
}
if (num4X4W == 2)
{
bmiSpan[j + 1] = bmiSpan[j];
mi.Bmi[j + 1] = mi.Bmi[j];
}
}
}
mi.Mode = bMode;
bmiSpan[3].Mv.AsSpan().CopyTo(mi.Mv.AsSpan());
CopyPair(ref mi.Mv, ref mi.Bmi[3].Mv);
}
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);
}
}
@@ -1093,42 +1082,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.Value.SegmentId, ref r);
mi.Value.TxSize = ReadTxSize(ref cm, ref xd, true, ref r);
refFrameSpan[0] = Constants.IntraFrame;
refFrameSpan[1] = Constants.None;
mi.Value.RefFrame[0] = Constants.IntraFrame;
mi.Value.RefFrame[1] = Constants.None;
Span<BModeInfo> bmiSpan;
switch (bsize)
{
case BlockSize.Block4X4:
bmiSpan = mi.Value.Bmi.AsSpan();
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));
}
mi.Value.Mode = bmiSpan[3].Mode;
mi.Value.Mode = mi.Value.Bmi[3].Mode;
break;
case BlockSize.Block4X8:
bmiSpan = mi.Value.Bmi.AsSpan();
bmiSpan[0].Mode = bmiSpan[2].Mode =
mi.Value.Bmi[0].Mode = mi.Value.Bmi[2].Mode =
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));
break;
case BlockSize.Block8X4:
bmiSpan = mi.Value.Bmi.AsSpan();
bmiSpan[0].Mode = bmiSpan[1].Mode =
mi.Value.Bmi[0].Mode = mi.Value.Bmi[1].Mode =
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));
break;
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());
}
private static void CopyRefFramePair(ref Array2<sbyte> dst, ref Array2<sbyte> src)
{
dst[0] = src[0];
dst[1] = src[1];
}
public static void ReadModeInfo(
ref TileWorkerData twd,
ref Vp9Common cm,
@@ -1165,8 +1151,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (int w = 0; w < xMis; ++w)
{
ref MvRef mv = ref frameMvs[w];
mi.RefFrame.AsSpan().CopyTo(mv.RefFrame.AsSpan());
mi.Mv.AsSpan().CopyTo(mv.Mv.AsSpan());
CopyRefFramePair(ref mv.RefFrame, ref mi.RefFrame);
CopyPair(ref mv.Mv, ref mi.Mv);
}
frameMvs = frameMvs.Slice(cm.MiCols);

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
@@ -130,7 +129,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x)
{
byte* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan();
ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x)
{
byte* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan();
ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -276,7 +275,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y)
{
byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan();
ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -311,7 +310,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y)
{
byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan();
ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -620,7 +619,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x)
{
ushort* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan();
ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -656,7 +655,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int x = 0; x < w; ++x)
{
ushort* srcX = &src[xQ4 >> SubpelBits];
Span<short> xFilter = xFilters[xQ4 & SubpelMask].AsSpan();
ref Array8<short> xFilter = ref xFilters[xQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -693,7 +692,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y)
{
ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan();
ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0;
for (int k = 0; k < SubpelTaps; ++k)
{
@@ -729,7 +728,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
for (int y = 0; y < h; ++y)
{
ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride];
Span<short> yFilter = yFilters[yQ4 & SubpelMask].AsSpan();
ref Array8<short> yFilter = ref yFilters[yQ4 & SubpelMask];
int sum = 0;
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,
ReadOnlySpan<Array2<uint>> ct32X32P)
ref Array3<Array2<uint>> ct32X32P)
{
Span<uint> ct32X32PSpan0 = ct32X32P[0].AsSpan();
Span<uint> ct32X32PSpan1 = ct32X32P[1].AsSpan();
Span<uint> ct32X32PSpan2 = ct32X32P[2].AsSpan();
ct32X32PSpan0[0] = txCount32X32P[(int)TxSize.Tx4X4];
ct32X32PSpan0[1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] +
ct32X32P[0][0] = txCount32X32P[(int)TxSize.Tx4X4];
ct32X32P[0][1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] +
txCount32X32P[(int)TxSize.Tx32X32];
ct32X32PSpan1[0] = txCount32X32P[(int)TxSize.Tx8X8];
ct32X32PSpan1[1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32];
ct32X32PSpan2[0] = txCount32X32P[(int)TxSize.Tx16X16];
ct32X32PSpan2[1] = txCount32X32P[(int)TxSize.Tx32X32];
ct32X32P[1][0] = txCount32X32P[(int)TxSize.Tx8X8];
ct32X32P[1][1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32];
ct32X32P[2][0] = txCount32X32P[(int)TxSize.Tx16X16];
ct32X32P[2][1] = txCount32X32P[(int)TxSize.Tx32X32];
}
internal static void TxCountsToBranchCounts16X16(ReadOnlySpan<uint> txCount16X16P,
ReadOnlySpan<Array2<uint>> ct16X16P)
ref Array2<Array2<uint>> ct16X16P)
{
Span<uint> ct16X16PSpan0 = ct16X16P[0].AsSpan();
Span<uint> ct16X16PSpan1 = ct16X16P[1].AsSpan();
ct16X16PSpan0[0] = txCount16X16P[(int)TxSize.Tx4X4];
ct16X16PSpan0[1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16];
ct16X16PSpan1[0] = txCount16X16P[(int)TxSize.Tx8X8];
ct16X16PSpan1[1] = txCount16X16P[(int)TxSize.Tx16X16];
ct16X16P[0][0] = txCount16X16P[(int)TxSize.Tx4X4];
ct16X16P[0][1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16];
ct16X16P[1][0] = txCount16X16P[(int)TxSize.Tx8X8];
ct16X16P[1][1] = txCount16X16P[(int)TxSize.Tx16X16];
}
internal static void TxCountsToBranchCounts8X8(ReadOnlySpan<uint> txCount8X8P,
ReadOnlySpan<Array2<uint>> ct8X8P)
ref Array1<Array2<uint>> ct8X8P)
{
Span<uint> ct8X8PSpan = ct8X8P[0].AsSpan();
ct8X8PSpan[0] = txCount8X8P[(int)TxSize.Tx4X4];
ct8X8PSpan[1] = txCount8X8P[(int)TxSize.Tx8X8];
ct8X8P[0][0] = txCount8X8P[(int)TxSize.Tx4X4];
ct8X8P[0][1] = txCount8X8P[(int)TxSize.Tx8X8];
}
public static unsafe void SetupPastIndependence(ref Vp9Common cm)

View File

@@ -378,11 +378,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
int index = shiftY;
Span<byte> lflYSpan = lfm.LflY.AsSpan();
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;
}
@@ -446,30 +444,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
const ulong AboveBorder = 0x000000ff000000ffUL;
const ushort LeftBorderUv = 0x1111;
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
// for 32x32 transforms also.
leftYSpan[(int)TxSize.Tx16X16] |= leftYSpan[(int)TxSize.Tx32X32];
aboveYSpan[(int)TxSize.Tx16X16] |= aboveYSpan[(int)TxSize.Tx32X32];
leftUvSpan[(int)TxSize.Tx16X16] |= leftUvSpan[(int)TxSize.Tx32X32];
aboveUvSpan[(int)TxSize.Tx16X16] |= aboveUvSpan[(int)TxSize.Tx32X32];
lfm.LeftY[(int)TxSize.Tx16X16] |= lfm.LeftY[(int)TxSize.Tx32X32];
lfm.AboveY[(int)TxSize.Tx16X16] |= lfm.AboveY[(int)TxSize.Tx32X32];
lfm.LeftUv[(int)TxSize.Tx16X16] |= lfm.LeftUv[(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
// is 4x4. So if the 4x4 is set on a border pixel add it to the 8x8 and
// remove it from the 4x4.
leftYSpan[(int)TxSize.Tx8X8] |= leftYSpan[(int)TxSize.Tx4X4] & LeftBorder;
leftYSpan[(int)TxSize.Tx4X4] &= ~LeftBorder;
aboveYSpan[(int)TxSize.Tx8X8] |= aboveYSpan[(int)TxSize.Tx4X4] & AboveBorder;
aboveYSpan[(int)TxSize.Tx4X4] &= ~AboveBorder;
leftUvSpan[(int)TxSize.Tx8X8] |= (ushort)(leftUvSpan[(int)TxSize.Tx4X4] & LeftBorderUv);
leftUvSpan[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv);
aboveUvSpan[(int)TxSize.Tx8X8] |= (ushort)(aboveUvSpan[(int)TxSize.Tx4X4] & AboveBorderUv);
aboveUvSpan[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv);
lfm.LeftY[(int)TxSize.Tx8X8] |= lfm.LeftY[(int)TxSize.Tx4X4] & LeftBorder;
lfm.LeftY[(int)TxSize.Tx4X4] &= ~LeftBorder;
lfm.AboveY[(int)TxSize.Tx8X8] |= lfm.AboveY[(int)TxSize.Tx4X4] & AboveBorder;
lfm.AboveY[(int)TxSize.Tx4X4] &= ~AboveBorder;
lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx4X4] & LeftBorderUv);
lfm.LeftUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv);
lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx4X4] & AboveBorderUv);
lfm.AboveUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv);
// We do some special edge handling.
if (miRow + Constants.MiBlockSize > cm.MiRows)
@@ -483,10 +476,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Remove values completely outside our border.
for (int i = 0; i < (int)TxSize.Tx32X32; i++)
{
leftYSpan[i] &= maskY;
aboveYSpan[i] &= maskY;
leftUvSpan[i] &= maskUv;
aboveUvSpan[i] &= maskUv;
lfm.LeftY[i] &= maskY;
lfm.AboveY[i] &= maskY;
lfm.LeftUv[i] &= maskUv;
lfm.AboveUv[i] &= maskUv;
}
lfm.Int4X4Y &= maskY;
@@ -496,14 +489,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// apply the shorter one instead.
if (rows == 1)
{
aboveUvSpan[(int)TxSize.Tx8X8] |= aboveUvSpan[(int)TxSize.Tx16X16];
aboveUvSpan[(int)TxSize.Tx16X16] = 0;
lfm.AboveUv[(int)TxSize.Tx8X8] |= lfm.AboveUv[(int)TxSize.Tx16X16];
lfm.AboveUv[(int)TxSize.Tx16X16] = 0;
}
if (rows == 5)
{
aboveUvSpan[(int)TxSize.Tx8X8] |= (ushort)(aboveUvSpan[(int)TxSize.Tx16X16] & 0xff00);
aboveUvSpan[(int)TxSize.Tx16X16] &= (ushort)~(aboveUvSpan[(int)TxSize.Tx16X16] & 0xff00);
lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(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.
for (int i = 0; i < (int)TxSize.Tx32X32; i++)
{
leftYSpan[i] &= maskY;
aboveYSpan[i] &= maskY;
leftUvSpan[i] &= maskUv;
aboveUvSpan[i] &= maskUv;
lfm.LeftY[i] &= maskY;
lfm.AboveY[i] &= maskY;
lfm.LeftUv[i] &= maskUv;
lfm.AboveUv[i] &= maskUv;
}
lfm.Int4X4Y &= maskY;
@@ -536,14 +529,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// apply the shorter one instead.
if (columns == 1)
{
leftUvSpan[(int)TxSize.Tx8X8] |= leftUvSpan[(int)TxSize.Tx16X16];
leftUvSpan[(int)TxSize.Tx16X16] = 0;
lfm.LeftUv[(int)TxSize.Tx8X8] |= lfm.LeftUv[(int)TxSize.Tx16X16];
lfm.LeftUv[(int)TxSize.Tx16X16] = 0;
}
if (columns == 5)
{
leftUvSpan[(int)TxSize.Tx8X8] |= (ushort)(leftUvSpan[(int)TxSize.Tx16X16] & 0xcccc);
leftUvSpan[(int)TxSize.Tx16X16] &= (ushort)~(leftUvSpan[(int)TxSize.Tx16X16] & 0xcccc);
lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(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++)
{
leftYSpan[i] &= 0xfefefefefefefefeUL;
leftUvSpan[i] &= 0xeeee;
lfm.LeftY[i] &= 0xfefefefefefefefeUL;
lfm.LeftUv[i] &= 0xeeee;
}
}
// 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((leftYSpan[(int)TxSize.Tx16X16] & leftYSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((leftYSpan[(int)TxSize.Tx8X8] & leftYSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Y & leftYSpan[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((leftUvSpan[(int)TxSize.Tx16X16] & leftUvSpan[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((leftUvSpan[(int)TxSize.Tx16X16] & leftUvSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((leftUvSpan[(int)TxSize.Tx8X8] & leftUvSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Uv & leftUvSpan[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((aboveYSpan[(int)TxSize.Tx16X16] & aboveYSpan[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((aboveYSpan[(int)TxSize.Tx16X16] & aboveYSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((aboveYSpan[(int)TxSize.Tx8X8] & aboveYSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Y & aboveYSpan[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((aboveUvSpan[(int)TxSize.Tx16X16] & aboveUvSpan[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((aboveUvSpan[(int)TxSize.Tx16X16] & aboveUvSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((aboveUvSpan[(int)TxSize.Tx8X8] & aboveUvSpan[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Uv & aboveUvSpan[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.LeftY[(int)TxSize.Tx8X8] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Y & lfm.LeftY[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.LeftUv[(int)TxSize.Tx8X8] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Uv & lfm.LeftUv[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.AboveY[(int)TxSize.Tx8X8] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Y & lfm.AboveY[(int)TxSize.Tx16X16]) == 0);
Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx8X8]) == 0);
Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.AboveUv[(int)TxSize.Tx8X8] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0);
Debug.Assert((lfm.Int4X4Uv & lfm.AboveUv[(int)TxSize.Tx16X16]) == 0);
}
public static unsafe void ResetLfm(ref Vp9Common cm)
@@ -590,8 +583,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
int lvl;
Span<LoopFilterThresh> lFThrSpan = lfi.Lfthr.AsSpan();
// For each possible value for the loop filter fill out limits
for (lvl = 0; lvl <= MaxLoopFilter; lvl++)
{
@@ -611,8 +602,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
blockInsideLimit = 1;
}
lFThrSpan[lvl].Lim.AsSpan().Fill((byte)blockInsideLimit);
lFThrSpan[lvl].Mblim.AsSpan().Fill((byte)((2 * (lvl + 2)) + blockInsideLimit));
lfi.Lfthr[lvl].Lim.AsSpan().Fill((byte)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;
}
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++)
{
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
// 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
{
int refr, mode;
int intraLvl = lvlSeg + (intraFrameRefDelta * scale);
lvlSpan[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter);
Span<Array2<byte>> lvlSpan2 = lvlSpan[segId].AsSpan();
int intraLvl = lvlSeg + (lf.RefDeltas[Constants.IntraFrame] * scale);
lfi.Lvl[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter);
for (refr = Constants.LastFrame; refr < Constants.MaxRefFrames; ++refr)
{
Span<byte> lvlSpan3 = lvlSpan2[refr].AsSpan();
for (mode = 0; mode < MaxModeLfDeltas; ++mode)
{
int interLvl = lvlSeg + (refDeltasSpan[refr] * scale) + (modeDeltasSpan[mode] * scale);
lvlSpan3[mode] = (byte)Math.Clamp(interLvl, 0, MaxLoopFilter);
int interLvl = lvlSeg + (lf.RefDeltas[refr] * scale) + (lf.ModeDeltas[mode] * scale);
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]];
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) == dualOne)
{
LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, mblim0Span[0], lim0Span[0],
hevThr0Span[0], bd);
LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, lfis[0].Mblim[0], lfis[0].Lim[0],
lfis[0].HevThr[0], bd);
}
else
{
if ((mask16X16 & 1) == 0)
{
LoopFilterScalar.HighBdLpfVertical16(ss[1], pitch, mblim1Span[0],
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical16(ss[0], pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
ref LoopFilterThresh lfi = ref lfis[(mask16X16 & 1) == 0 ? 1 : 0];
LoopFilterScalar.HighBdLpfVertical16(ss[(mask16X16 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0],
lfi.Lim[0], lfi.HevThr[0], bd);
}
}
@@ -865,26 +833,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfVertical8Dual(
ss[0],
pitch,
mblim0Span[0],
lim0Span[0],
hevThr0Span[0],
mblim1Span[0],
lim1Span[0],
hevThr1Span[0],
lfis[0].Mblim[0],
lfis[0].Lim[0],
lfis[0].HevThr[0],
lfis[1].Mblim[0],
lfis[1].Lim[0],
lfis[1].HevThr[0],
bd);
}
else
{
if ((mask8X8 & 1) == 0)
{
LoopFilterScalar.HighBdLpfVertical8(ss[1], pitch, mblim1Span[0],
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical8(ss[0], pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
ref LoopFilterThresh lfi = ref lfis[(mask8X8 & 1) == 0 ? 1 : 0];
LoopFilterScalar.HighBdLpfVertical8(
ss[(mask8X8 & 1) == 0 ? 1 : 0],
pitch,
lfi.Mblim[0],
lfi.Lim[0],
lfi.HevThr[0],
bd);
}
}
@@ -895,26 +861,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfVertical4Dual(
ss[0],
pitch,
mblim0Span[0],
lim0Span[0],
hevThr0Span[0],
mblim1Span[0],
lim1Span[0],
hevThr1Span[0],
lfis[0].Mblim[0],
lfis[0].Lim[0],
lfis[0].HevThr[0],
lfis[1].Mblim[0],
lfis[1].Lim[0],
lfis[1].HevThr[0],
bd);
}
else
{
if ((mask4X4 & 1) == 0)
{
LoopFilterScalar.HighBdLpfVertical4(ss[1], pitch, mblim1Span[0],
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical4(ss[0], pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
ref LoopFilterThresh lfi = ref lfis[(mask4X4 & 1) == 0 ? 1 : 0];
LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0],
lfi.Lim[0], lfi.HevThr[0], bd);
}
}
@@ -925,26 +884,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfVertical4Dual(
ss[0].Slice(4),
pitch,
mblim0Span[0],
lim0Span[0],
hevThr0Span[0],
mblim1Span[0],
lim1Span[0],
hevThr1Span[0],
lfis[0].Mblim[0],
lfis[0].Lim[0],
lfis[0].HevThr[0],
lfis[1].Mblim[0],
lfis[1].Lim[0],
lfis[1].HevThr[0],
bd);
}
else
{
if ((mask4X4Int & 1) == 0)
{
LoopFilterScalar.HighBdLpfVertical4(ss[1].Slice(4), pitch, mblim1Span[0],
lim1Span[0], hevThr1Span[0], bd);
}
else
{
LoopFilterScalar.HighBdLpfVertical4(ss[0].Slice(4), pitch, mblim0Span[0],
lim0Span[0], hevThr0Span[0], bd);
}
ref LoopFilterThresh lfi = ref lfis[(mask4X4Int & 1) == 0 ? 1 : 0];
LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4Int & 1) == 0 ? 1 : 0].Slice(4), pitch,
lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd);
}
}
}
@@ -1134,22 +1086,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
if ((mask & 1) != 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 & 3) == 3)
{
LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, mblimSpan[0], limSpan[0],
hevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, lfi.Mblim[0], lfi.Lim[0],
lfi.HevThr[0], bd);
count = 2;
}
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);
}
}
@@ -1159,20 +1107,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
// Next block's thresholds.
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(
s,
pitch,
mblimSpan[0],
limSpan[0],
hevThrSpan[0],
nMblimSpan[0],
nLimSpan[0],
nHevThrSpan[0],
lfi.Mblim[0],
lfi.Lim[0],
lfi.HevThr[0],
lfin.Mblim[0],
lfin.Lim[0],
lfin.HevThr[0],
bd);
if ((mask4X4Int & 3) == 3)
@@ -1180,36 +1124,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfHorizontal4Dual(
s.Slice(4 * pitch),
pitch,
mblimSpan[0],
limSpan[0],
hevThrSpan[0],
nMblimSpan[0],
nLimSpan[0],
nHevThrSpan[0],
lfi.Mblim[0],
lfi.Lim[0],
lfi.HevThr[0],
lfin.Mblim[0],
lfin.Lim[0],
lfin.HevThr[0],
bd);
}
else if ((mask4X4Int & 1) != 0)
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0],
limSpan[0], hevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
lfi.Lim[0], lfi.HevThr[0], bd);
}
else if ((mask4X4Int & 2) != 0)
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, nMblimSpan[0],
nLimSpan[0], nHevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0],
lfin.Lim[0], lfin.HevThr[0], bd);
}
count = 2;
}
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);
if ((mask4X4Int & 1) != 0)
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0],
limSpan[0], hevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
lfi.Lim[0], lfi.HevThr[0], bd);
}
}
}
@@ -1219,20 +1163,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
// Next block's thresholds.
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(
s,
pitch,
mblimSpan[0],
limSpan[0],
hevThrSpan[0],
nMblimSpan[0],
nLimSpan[0],
nHevThrSpan[0],
lfi.Mblim[0],
lfi.Lim[0],
lfi.HevThr[0],
lfin.Mblim[0],
lfin.Lim[0],
lfin.HevThr[0],
bd);
if ((mask4X4Int & 3) == 3)
@@ -1240,43 +1180,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
LoopFilterScalar.HighBdLpfHorizontal4Dual(
s.Slice(4 * pitch),
pitch,
mblimSpan[0],
limSpan[0],
hevThrSpan[0],
nMblimSpan[0],
nLimSpan[0],
nHevThrSpan[0],
lfi.Mblim[0],
lfi.Lim[0],
lfi.HevThr[0],
lfin.Mblim[0],
lfin.Lim[0],
lfin.HevThr[0],
bd);
}
else if ((mask4X4Int & 1) != 0)
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0],
limSpan[0], hevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
lfi.Lim[0], lfi.HevThr[0], bd);
}
else if ((mask4X4Int & 2) != 0)
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, nMblimSpan[0],
nLimSpan[0], nHevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0],
lfin.Lim[0], lfin.HevThr[0], bd);
}
count = 2;
}
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);
if ((mask4X4Int & 1) != 0)
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0],
limSpan[0], hevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0],
lfi.Lim[0], lfi.HevThr[0], bd);
}
}
}
else
{
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], limSpan[0],
hevThrSpan[0], bd);
LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], lfi.Lim[0],
lfi.HevThr[0], bd);
}
}
@@ -1351,30 +1291,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
for (uint mask = mask16X16 | mask8X8 | mask4X4 | mask4X4Int; mask != 0; mask >>= 1)
{
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 ((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)
{
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)
{
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)
{
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);
@@ -1619,10 +1555,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{
ref Buf2D dst = ref plane.Dst;
ArrayPtr<byte> dst0 = dst.Buf;
Span<ulong> leftYSpan = lfm.LeftY.AsSpan();
ulong mask16X16 = leftYSpan[(int)TxSize.Tx16X16];
ulong mask8X8 = leftYSpan[(int)TxSize.Tx8X8];
ulong mask4X4 = leftYSpan[(int)TxSize.Tx4X4];
ulong mask16X16 = lfm.LeftY[(int)TxSize.Tx16X16];
ulong mask8X8 = lfm.LeftY[(int)TxSize.Tx8X8];
ulong mask4X4 = lfm.LeftY[(int)TxSize.Tx4X4];
ulong mask4X4Int = lfm.Int4X4Y;
Debug.Assert(plane.SubsamplingX == 0 && plane.SubsamplingY == 0);
@@ -1669,10 +1604,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Horizontal pass
dst.Buf = dst0;
Span<ulong> aboveYSpan = lfm.AboveY.AsSpan();
mask16X16 = aboveYSpan[(int)TxSize.Tx16X16];
mask8X8 = aboveYSpan[(int)TxSize.Tx8X8];
mask4X4 = aboveYSpan[(int)TxSize.Tx4X4];
mask16X16 = lfm.AboveY[(int)TxSize.Tx16X16];
mask8X8 = lfm.AboveY[(int)TxSize.Tx8X8];
mask4X4 = lfm.AboveY[(int)TxSize.Tx4X4];
mask4X4Int = lfm.Int4X4Y;
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;
Span<byte> lflUv = stackalloc byte[16];
Span<byte> lflY = lfm.LflY.AsSpan();
Span<ushort> leftUvSpan = lfm.LeftUv.AsSpan();
ushort mask16X16 = leftUvSpan[(int)TxSize.Tx16X16];
ushort mask8X8 = leftUvSpan[(int)TxSize.Tx8X8];
ushort mask4X4 = leftUvSpan[(int)TxSize.Tx4X4];
ushort mask16X16 = lfm.LeftUv[(int)TxSize.Tx16X16];
ushort mask8X8 = lfm.LeftUv[(int)TxSize.Tx8X8];
ushort mask4X4 = lfm.LeftUv[(int)TxSize.Tx4X4];
ushort mask4X4Int = lfm.Int4X4Uv;
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++)
{
lflUv[(r << 1) + c] = lflY[(r << 3) + (c << 1)];
lflUv[((r + 2) << 1) + c] = lflY[((r + 2) << 3) + (c << 1)];
lflUv[(r << 1) + c] = lfm.LflY[(r << 3) + (c << 1)];
lflUv[((r + 2) << 1) + c] = lfm.LflY[((r + 2) << 3) + (c << 1)];
}
if (cm.UseHighBitDepth)
@@ -1793,10 +1725,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
// Horizontal pass
dst.Buf = dst0;
Span<ushort> aboveUvSpan = lfm.AboveUv.AsSpan();
mask16X16 = aboveUvSpan[(int)TxSize.Tx16X16];
mask8X8 = aboveUvSpan[(int)TxSize.Tx8X8];
mask4X4 = aboveUvSpan[(int)TxSize.Tx4X4];
mask16X16 = lfm.AboveUv[(int)TxSize.Tx16X16];
mask8X8 = lfm.AboveUv[(int)TxSize.Tx8X8];
mask4X4 = lfm.AboveUv[(int)TxSize.Tx4X4];
mask4X4Int = lfm.Int4X4Uv;
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;
LfPath path;
int miRow, miCol;
Span<MacroBlockDPlane> planesSpan = planes.AsSpan();
if (yOnly)
{
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;
}
else if (planesSpan[1].SubsamplingY == 0 && planesSpan[1].SubsamplingX == 0)
else if (planes[1].SubsamplingY == 0 && planes[1].SubsamplingX == 0)
{
path = LfPath.LfPath444;
}
@@ -1907,23 +1837,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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]);
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)
{
switch (path)
{
case LfPath.LfPath420:
FilterBlockPlaneSs11(ref cm, ref planesSpan[plane], miRow, ref lfm[0]);
FilterBlockPlaneSs11(ref cm, ref planes[plane], miRow, ref lfm[0]);
break;
case LfPath.LfPath444:
FilterBlockPlaneSs00(ref cm, ref planesSpan[plane], miRow, ref lfm[0]);
FilterBlockPlaneSs00(ref cm, ref planes[plane], miRow, ref lfm[0]);
break;
case LfPath.LfPathSlow:
FilterBlockPlaneNon420(ref cm, ref planesSpan[plane], mi.Slice(miCol), miRow,
FilterBlockPlaneNon420(ref cm, ref planes[plane], mi.Slice(miCol), miRow,
miCol);
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)
{
Array8<short> output = new();
Span<short> outputSpan = output.AsSpan();
outputSpan[0] = e0;
outputSpan[1] = e1;
outputSpan[2] = e2;
outputSpan[3] = e3;
outputSpan[4] = e4;
outputSpan[5] = e5;
outputSpan[6] = e6;
outputSpan[7] = e7;
Array8<short> output = new()
{
[0] = e0,
[1] = e1,
[2] = e2,
[3] = e3,
[4] = e4,
[5] = e5,
[6] = e6,
[7] = e7
};
return output;
}

View File

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

View File

@@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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]);
uint count = Math.Min(ct[0] + ct[1], countSat);
@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
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];
if (den == 0)
@@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
Array2<uint> ct = new();
ct[0] = leftCount;
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;
}

View File

@@ -164,7 +164,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
}
public static void SetupDstPlanes(
Span<MacroBlockDPlane> planes,
ref Array3<MacroBlockDPlane> planes,
ref Surface src,
int miRow,
int miCol)
@@ -205,11 +205,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
strides[1] = src.UvStride;
strides[2] = src.UvStride;
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
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,
pd.SubsamplingY);
}

View File

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

View File

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

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