Compare commits

...

60 Commits

Author SHA1 Message Date
Coxxs
df40a69872 Fix headless mode (ryubing/ryujinx!146)
See merge request ryubing/ryujinx!146
2025-09-10 11:43:50 -05:00
LotP
b000f91dad Memory changes 2.2.1 (ryubing/ryujinx!144)
See merge request ryubing/ryujinx!144
2025-09-06 13:51:08 -05:00
LotP
a60b2a0ba3 Memory changes 2.2 (ryubing/ryujinx!143)
See merge request ryubing/ryujinx!143
2025-09-06 11:10:55 -05:00
Hack茶ん
4c9b48b754 Update Korean translation (ryubing/ryujinx!141)
See merge request ryubing/ryujinx!141
2025-09-05 13:06:59 -05:00
Neo
3bd7d5904e Compat: New Entries + Minor Adjustments (ryubing/ryujinx!140)
See merge request ryubing/ryujinx!140
2025-09-05 03:50:49 -05:00
GreemDev
23eb9a3043 improvement: Make the updater log a special error message in some cases
specifically about potentially not being connected to the internet on a connection error or name resolution error
2025-09-05 03:12:15 -05:00
GreemDev
931ec44406 [ci skip] fix: <Reset> text in Pokemon Scarlet/Violet play report rich presence 2025-09-05 03:02:16 -05:00
GreemDev
d68efa98ba [ci skip] Update NuGet packages 2025-09-05 02:25:05 -05:00
GreemDev
ded76801d1 removal: Installing keys from a zip
Also cleaned up a bit
2025-09-04 19:04:50 -05:00
GreemDev
6084df7473 Silksong compatibility entry and RPC image 2025-09-04 16:23:38 -05:00
LotP1
f3953c6039 Fix a crash when no controller is connected 2025-09-04 16:18:07 -05:00
LotP1
91f5247e7f add missing field 2025-09-04 16:17:59 -05:00
LotP1
5658402c6b fix spelling 2025-09-04 16:17:53 -05:00
LotP1
1b2c93e188 Add version comment 2025-09-04 16:14:38 -05:00
LotP1
9e599ff325 remove stub 2025-09-04 16:14:18 -05:00
LotP1
7a5f430b59 hle: Basic event handle implementation for IApplicationFunctions 210
Lets Hollow Knight: Silksong boot.
2025-09-04 16:14:03 -05:00
shinyoyo
1e340ce2f3 Update Simplified Chinese translation. (ryubing/ryujinx!139)
See merge request ryubing/ryujinx!139
2025-09-03 15:49:14 -05:00
WilliamWsyHK
dbb4e63e1e Update zh-TW translation (ryubing/ryujinx!130)
See merge request ryubing/ryujinx!130
2025-09-03 03:28:37 -05:00
Hack茶ん
d00ab52fa2 Update Korean translation (ryubing/ryujinx!138)
See merge request ryubing/ryujinx!138
2025-09-03 03:26:33 -05:00
VewDev
959af3613d doc: update documentation for Shared property in Switch class (ryubing/ryujinx!137)
See merge request ryubing/ryujinx!137
2025-09-03 03:25:43 -05:00
GreemDev
3309fb2351 chore: Replace dead URLs in the changelog with archive.org links (that I could find) 2025-09-03 00:49:30 -05:00
GreemDev
e1e8628a6f chore: remove BuildAndPushLibraries.sh 2025-09-03 00:25:08 -05:00
Neo
3969191605 UI: Menubar & Game Context Menu Updates (ryubing/ryujinx!134)
See merge request ryubing/ryujinx!134
2025-09-02 03:32:06 -05:00
Hack茶ん
07c7b39053 Update Korean translation (ryubing/ryujinx!136)
See merge request ryubing/ryujinx!136
2025-09-01 17:57:44 -05:00
Hack茶ん
053efaa414 Update Korean translation (ryubing/ryujinx!135)
See merge request ryubing/ryujinx!135
2025-09-01 01:36:27 -05:00
GreemDev
56e6339553 hle: cheats: Prevent NullRef and throw a TamperCompilationException instead
for null base instruction byte arrays on the current block in EndConditionalBlock
2025-08-31 23:06:42 -05:00
shinyoyo
042362ee2b Update Simplified Chinese translation. (ryubing/ryujinx!133)
See merge request ryubing/ryujinx!133
2025-08-30 22:40:05 -05:00
GreemDev
7347ee2212 [ci skip] chore: UI: Add localization key for LDN Game Viewer filters dropdown button heading 2025-08-30 22:13:38 -05:00
LotP
01a9b636af Memory changes 2.1 (ryubing/ryujinx!132)
See merge request ryubing/ryujinx!132
2025-08-30 20:30:17 -05:00
GreemDev
6e47d8548c feature: UI: LDN Games Viewer
This window can be accessed via "Help" menu in the title bar.
This menu's data is synced with the in-app-list LDN game data, and that has been modified to hide unjoinable games (in-progress and/or private (needing a passphrase)). You can still see these games in the list.
2025-08-30 19:54:00 -05:00
GreemDev
da340f5615 chore: remove redundant CloseWindow helper 2025-08-30 00:40:39 -05:00
GreemDev
be249f7bdc chore: move NFC tags URL to SharedConstants.cs 2025-08-30 00:35:16 -05:00
GreemDev
462c93e1ff fix key number 5 locale 2025-08-28 20:32:48 -05:00
Babib3l
573a6f32fe nullify + update spanish and french translations (ryubing/ryujinx!125)
See merge request ryubing/ryujinx!125
2025-08-28 13:28:24 -05:00
GreemDev
7846f58cad [ci skip] chore: Change LDN server URL (it's the same server, just a more official URL) 2025-08-27 22:49:51 -05:00
GreemDev
0203065fed ui: fix: Missing "Loading" text when shader cache is disabled and PPTC doesn't trigger 2025-08-27 22:35:09 -05:00
shinyoyo
7319a2dafc Nullify & update Simplified Chinese translation. (ryubing/ryujinx!124)
See merge request ryubing/ryujinx!124
2025-08-27 00:41:47 -05:00
Hack茶ん
f992735656 Nullify & Update Korean translation (ryubing/ryujinx!122)
See merge request ryubing/ryujinx!122
2025-08-27 00:40:11 -05:00
GreemDev
48b9f2ab93 docs: compat: High on Life: Menus 2025-08-26 20:12:36 -05:00
LotP
50ab108ee1 Memory Changes part 2 (ryubing/ryujinx!123)
See merge request ryubing/ryujinx!123
2025-08-25 17:44:15 -05:00
VewDev
d499449f57 feat(ui): improve Amiibo selection UX (ryubing/ryujinx!121)
See merge request ryubing/ryujinx!121
2025-08-25 05:14:06 -05:00
GreemDev
cd3c614021 fix: part 2: Resolve AppImage CI failures in Stable and Canary
This was done by adding the -n flag when running appimagetool.
Fix suggested by `settyness` on Discord.
2025-08-24 15:44:38 -05:00
GreemDev
5fa82bb1f5 fix: attempt at resolving AppImage CI failures 2025-08-24 15:35:11 -05:00
GreemDev
234cb99325 [ci skip] chore: minor nitpick: Use passed dlc IDs array instead of the field 2025-08-23 23:50:23 -05:00
GreemDev
ab7914f235 input: ava: Rename timer interval constant
Also cut the delay after which scrolling is considered ended in half
2025-08-19 19:39:18 -05:00
MaxLastBreath
3df6b7c0f5 Fix Avalonia Native MouseWheel-Support (ryubing/ryujinx!116)
See merge request ryubing/ryujinx!116
2025-08-19 18:46:20 -05:00
ProIcons
37e81481c4 Fix nn::ec::detail::PurchasedProductInfo to return No purchase information... (ryubing/ryujinx!114)
See merge request ryubing/ryujinx!114
2025-08-17 04:52:20 -05:00
shinyoyo
4d8b799763 Updated Simplified Chinese translation. (ryubing/ryujinx!104)
See merge request ryubing/ryujinx!104
2025-08-16 19:18:39 -05:00
WilliamWsyHK
cb786b7147 Update zh-TW translation after nullify locales to signify intention of using default en-US values (ryubing/ryujinx!85)
See merge request ryubing/ryujinx!85
2025-08-16 19:17:51 -05:00
Gab
2a308f50c0 Nullify french locales + updated translation (ryubing/ryujinx!87)
See merge request ryubing/ryujinx!87
2025-08-16 16:59:20 -05:00
Neo
b51c5cead6 Nullify + Update Russian Locales (ryubing/ryujinx!103)
See merge request ryubing/ryujinx!103
2025-08-13 05:00:22 -05:00
GreemDev
461c1f5342 UI: compat list: fix squished search box 2025-08-13 04:08:28 -05:00
Neo
cfea61b3a0 QUICK FIX: Compatibility Window Checkbox Spacing (ryubing/ryujinx!112)
See merge request ryubing/ryujinx!112
2025-08-13 03:53:58 -05:00
Neo
ae2e9a73ab UI Updates Batch 2 (ryubing/ryujinx!105)
See merge request ryubing/ryujinx!105
2025-08-12 17:45:24 -05:00
GreemDev
c6f22318a7 add an ASCII header at startup in the log 2025-08-11 18:06:53 -05:00
GreemDev
dd5e1b99b1 remove localization entries for auto graphics backend 2025-08-11 18:00:10 -05:00
Hack茶ん
c863ffd353 Update Korean translation (ryubing/ryujinx!107)
See merge request ryubing/ryujinx!107
2025-08-10 16:37:14 -05:00
LotP
d6d089b81b Revert "Fix crash caused by VirtualRange mismatch (ryubing/ryujinx!109)" (ryubing/ryujinx!110)
See merge request ryubing/ryujinx!110
2025-08-09 18:41:36 -05:00
LotP
c482b7a1c0 Fix crash caused by VirtualRange mismatch (ryubing/ryujinx!109)
See merge request ryubing/ryujinx!109
2025-08-09 17:46:29 -05:00
在中国的泰国青年_
01e1cd4d5a update thai language in locales.json (ryubing/ryujinx!102)
See merge request ryubing/ryujinx!102
2025-08-08 04:34:56 -05:00
163 changed files with 6346 additions and 3817 deletions

View File

@@ -102,51 +102,49 @@ jobs:
chmod +x Ryujinx.sh Ryujinx 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 tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd 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" gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
shell: bash shell: bash
# If anyone wants to look into why appimagetool randomly errors with exit code 8, that would be cool - name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
# - name: Build AppImage (Linux) run: |
# if: matrix.platform.os == 'ubuntu-latest' BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
# run: | PLATFORM_NAME="${{ matrix.platform.name }}"
# BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
# PLATFORM_NAME="${{ matrix.platform.name }}" sudo apt install -y zsync desktop-file-utils appstream
#
# sudo apt install -y zsync desktop-file-utils appstream mkdir -p tools
# export PATH="$PATH:$(readlink -f tools)"
# mkdir -p tools
# export PATH="$PATH:$(readlink -f tools)" # Setup appimagetool
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
# # Setup appimagetool chmod +x tools/appimagetool
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" chmod +x distribution/linux/appimage/build-appimage.sh
# chmod +x tools/appimagetool
# chmod +x distribution/linux/appimage/build-appimage.sh # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then
# # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name) ARCH_NAME=x64
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then export ARCH=x86_64
# ARCH_NAME=x64 elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
# export ARCH=x86_64 ARCH_NAME=arm64
# elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then export ARCH=aarch64
# ARCH_NAME=arm64 else
# export ARCH=aarch64 echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
# else exit 1
# echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME"" fi
# exit 1
# fi export UFLAG="gh-releases-zsync|${{ secrets.RC_OWNER }}${{ secrets.RC_CANARY_NAME }}|latest|*-$ARCH_NAME.AppImage.zsync"
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
# export UFLAG="gh-releases-zsync|${{ secrets.RC_OWNER }}${{ secrets.RC_CANARY_NAME }}|latest|*-$ARCH_NAME.AppImage.zsync"
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh pushd publish_appimage
# mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
# pushd publish_appimage mv Ryujinx.AppImage.zsync ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
# mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage popd
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
# popd 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"
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage" shell: bash
# 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
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal

View File

@@ -96,48 +96,46 @@ jobs:
shell: bash shell: bash
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt install -y zsync desktop-file-utils appstream
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
chmod +x distribution/linux/appimage/build-appimage.sh
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
ARCH_NAME=x64
export ARCH=x86_64
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
ARCH_NAME=arm64
export ARCH=aarch64
else
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
exit 1
fi
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
popd
# If anyone wants to look into why appimagetool randomly errors with exit code 8, that would be cool gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
# - name: Build AppImage (Linux) shell: bash
# if: matrix.platform.os == 'ubuntu-latest'
# run: |
# BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
# PLATFORM_NAME="${{ matrix.platform.name }}"
#
# sudo apt install -y zsync desktop-file-utils appstream
#
# mkdir -p tools
# export PATH="$PATH:$(readlink -f tools)"
#
# # Setup appimagetool
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
# chmod +x tools/appimagetool
# chmod +x distribution/linux/appimage/build-appimage.sh
#
# # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then
# ARCH_NAME=x64
# export ARCH=x86_64
# elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
# ARCH_NAME=arm64
# export ARCH=aarch64
# else
# echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
# exit 1
# fi
#
# export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
#
# pushd publish_appimage
# mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
# popd
#
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
# shell: bash
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal

View File

@@ -1,18 +0,0 @@
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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -978,7 +978,7 @@
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07 0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19 010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
01008CB01E52E000,"DOOM + DOOM II",opengl;ldn-untested;LAN,playable,2024-09-12 07:06:01 01008CB01E52E000,"DOOM + DOOM II",opengl;ldn-untested;LAN,playable,2024-09-12 07:06:01
010029D00E740000,"DOOM 3",crash,menus,2024-08-03 05:25:47 010029D00E740000,"DOOM 3",crash;slow,menus,2024-08-03 05:25:47
01005D700E742000,"DOOM 64",nvdec;vulkan,playable,2020-10-13 23:47:28 01005D700E742000,"DOOM 64",nvdec;vulkan,playable,2020-10-13 23:47:28
0100D4F00DD02000,"DOOM II (Classic)",nvdec;online,playable,2021-06-03 20:10:01 0100D4F00DD02000,"DOOM II (Classic)",nvdec;online,playable,2021-06-03 20:10:01
0100B1A00D8CE000,"DOOM® Eternal",gpu;slow;nvdec;online-broken,ingame,2024-08-28 15:57:17 0100B1A00D8CE000,"DOOM® Eternal",gpu;slow;nvdec;online-broken,ingame,2024-08-28 15:57:17
@@ -1097,7 +1097,7 @@
0100F9600E746000,"ESP Ra.De. Psi",audio;slow,ingame,2024-03-07 15:05:08 0100F9600E746000,"ESP Ra.De. Psi",audio;slow,ingame,2024-03-07 15:05:08
010073000FE18000,"Esports powerful pro yakyuu 2020",gpu;crash;Needs More Attention,ingame,2024-04-29 05:34:14 010073000FE18000,"Esports powerful pro yakyuu 2020",gpu;crash;Needs More Attention,ingame,2024-04-29 05:34:14
01004F9012FD8000,"Estranged: The Departure",nvdec;UE4,playable,2022-10-24 10:37:58 01004F9012FD8000,"Estranged: The Departure",nvdec;UE4,playable,2022-10-24 10:37:58
010018f01e0a0000,"Eternights",,playable,2025-07-30 12:10:24 010018F01E0A0000,"Eternights",,playable,2025-07-30 12:10:24
0100CB900B498000,"Eternum Ex",,playable,2021-01-13 20:28:32 0100CB900B498000,"Eternum Ex",,playable,2021-01-13 20:28:32
010092501EB2C000,"Europa (Demo)",gpu;crash;UE4,ingame,2024-04-23 10:47:12 010092501EB2C000,"Europa (Demo)",gpu;crash;UE4,ingame,2024-04-23 10:47:12
01007BE0160D6000,"EVE ghost enemies",gpu,ingame,2023-01-14 03:13:30 01007BE0160D6000,"EVE ghost enemies",gpu,ingame,2023-01-14 03:13:30
@@ -1243,7 +1243,7 @@
010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38 010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38
010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27 010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27
0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24 0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24
0100c4e018a24000,"FRONT MISSION 2: Remake",,playable,2025-07-30 12:11:23 0100C4E018A24000,"FRONT MISSION 2: Remake",,playable,2025-07-30 12:11:23
01007E6019872000,"FRONT MISSION 3: Remake",,playable,2025-07-30 12:12:02 01007E6019872000,"FRONT MISSION 3: Remake",,playable,2025-07-30 12:12:02
0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59 0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59
0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36 0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36
@@ -1440,6 +1440,7 @@
0100C2700E338000,"Heroland",,playable,2020-08-05 15:35:39 0100C2700E338000,"Heroland",,playable,2020-08-05 15:35:39
01007AC00E012000,"HexaGravity",,playable,2021-05-28 13:47:48 01007AC00E012000,"HexaGravity",,playable,2021-05-28 13:47:48
01004E800F03C000,"Hidden",slow,ingame,2022-10-05 10:56:53 01004E800F03C000,"Hidden",slow,ingame,2022-10-05 10:56:53
0100C1101EE5A000,"High on Life",,menus,2025-08-26 19:11:00
0100F6A00A684000,"Higurashi no Naku Koro ni Hō",audio,ingame,2021-09-18 14:40:28 0100F6A00A684000,"Higurashi no Naku Koro ni Hō",audio,ingame,2021-09-18 14:40:28
0100F8D0129F4000,"Himehibi 1 gakki - Princess Days",crash,nothing,2021-11-03 08:34:19 0100F8D0129F4000,"Himehibi 1 gakki - Princess Days",crash,nothing,2021-11-03 08:34:19
0100F3D008436000,"Hiragana Pixel Party",,playable,2021-01-14 08:36:50 0100F3D008436000,"Hiragana Pixel Party",,playable,2021-01-14 08:36:50
@@ -1449,6 +1450,7 @@
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35 0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58 0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56 0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
010013C00E930000,"Hollow Knight: Silksong",,playable,2025-09-04 17:23:22
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56 0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56 0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38 010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38
@@ -1887,7 +1889,7 @@
010097800EA20000,"Monster Energy Supercross - The Official Videogame 3",UE4;audout;nvdec;online,playable,2021-06-14 12:37:54 010097800EA20000,"Monster Energy Supercross - The Official Videogame 3",UE4;audout;nvdec;online,playable,2021-06-14 12:37:54
0100E9900ED74000,"Monster Farm",32-bit;nvdec,playable,2021-05-05 19:29:13 0100E9900ED74000,"Monster Farm",32-bit;nvdec,playable,2021-05-05 19:29:13
0100770008DD8000,"Monster Hunter Generations Ultimate™",32-bit;online-broken;ldn-works,playable,2024-03-18 14:35:36 0100770008DD8000,"Monster Hunter Generations Ultimate™",32-bit;online-broken;ldn-works,playable,2024-03-18 14:35:36
0100B04011742000,"Monster Hunter Rise",gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works,ingame,2024-08-24 11:04:59 0100B04011742000,"MONSTER HUNTER RISE",gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works,ingame,2024-08-24 11:04:59
010093A01305C000,"Monster Hunter Rise Demo",online-broken;ldn-works;demo,playable,2022-10-18 23:04:17 010093A01305C000,"Monster Hunter Rise Demo",online-broken;ldn-works;demo,playable,2022-10-18 23:04:17
0100E21011446000,"Monster Hunter Stories 2: Wings of Ruin",services,ingame,2022-07-10 19:27:30 0100E21011446000,"Monster Hunter Stories 2: Wings of Ruin",services,ingame,2022-07-10 19:27:30
010042501329E000,"MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version",demo,playable,2022-11-13 22:20:26 010042501329E000,"MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version",demo,playable,2022-11-13 22:20:26
@@ -2312,7 +2314,7 @@
010077B00BDD8000,"Professional Farmer: Nintendo Switch™ Edition",slow,playable,2020-12-16 13:38:19 010077B00BDD8000,"Professional Farmer: Nintendo Switch™ Edition",slow,playable,2020-12-16 13:38:19
010018300C83A000,"Professor Lupo and his Horrible Pets",,playable,2020-06-12 00:08:45 010018300C83A000,"Professor Lupo and his Horrible Pets",,playable,2020-06-12 00:08:45
0100D1F0132F6000,"Professor Lupo: Ocean",,playable,2021-04-14 16:33:33 0100D1F0132F6000,"Professor Lupo: Ocean",,playable,2021-04-14 16:33:33
0100c3a017834000,"Prodeus",,playable,2025-07-30 12:07:52 0100C3A017834000,"Prodeus",,playable,2025-07-30 12:07:52
0100BBD00976C000,"Project Highrise: Architect's Edition",,playable,2022-08-10 17:19:12 0100BBD00976C000,"Project Highrise: Architect's Edition",,playable,2022-08-10 17:19:12
0100ACE00DAB6000,"Project Nimbus: Complete Edition",nvdec;UE4;vulkan-backend-bug,playable,2022-08-10 17:35:43 0100ACE00DAB6000,"Project Nimbus: Complete Edition",nvdec;UE4;vulkan-backend-bug,playable,2022-08-10 17:35:43
01002980140F6000,"Project TRIANGLE STRATEGY™ Debut Demo",UE4;demo,playable,2022-10-24 21:40:27 01002980140F6000,"Project TRIANGLE STRATEGY™ Debut Demo",UE4;demo,playable,2022-10-24 21:40:27
@@ -2578,6 +2580,7 @@
0100C610154CA000,"Shadowrun: Hong Kong - Extended Edition",gpu;Needs Update,ingame,2022-10-04 20:53:09 0100C610154CA000,"Shadowrun: Hong Kong - Extended Edition",gpu;Needs Update,ingame,2022-10-04 20:53:09
010000000EEF0000,"Shadows 2: Perfidia",,playable,2020-08-07 12:43:46 010000000EEF0000,"Shadows 2: Perfidia",,playable,2020-08-07 12:43:46
0100AD700CBBE000,"Shadows of Adam",,playable,2021-01-11 13:35:58 0100AD700CBBE000,"Shadows of Adam",,playable,2021-01-11 13:35:58
010037A01F96C000,"Shadows of the Damned: Hella Remastered",,playable,2025-09-05 11:34:32
01002A800C064000,"Shadowverse Champions Battle",,playable,2022-10-02 22:59:29 01002A800C064000,"Shadowverse Champions Battle",,playable,2022-10-02 22:59:29
01003B90136DA000,"Shadowverse: Champion's Battle",crash,nothing,2023-03-06 00:31:50 01003B90136DA000,"Shadowverse: Champion's Battle",crash,nothing,2023-03-06 00:31:50
0100820013612000,"Shady Part of Me",,playable,2022-10-20 11:31:55 0100820013612000,"Shady Part of Me",,playable,2022-10-20 11:31:55
@@ -2976,6 +2979,7 @@
0100EBA01548E000,"The Cruel King and the Great Hero",gpu;services,ingame,2022-12-02 07:02:08 0100EBA01548E000,"The Cruel King and the Great Hero",gpu;services,ingame,2022-12-02 07:02:08
010051800E922000,"The Dark Crystal: Age of Resistance Tactics",,playable,2020-08-11 13:43:41 010051800E922000,"The Dark Crystal: Age of Resistance Tactics",,playable,2020-08-11 13:43:41
01003DE00918E000,"The Darkside Detective",,playable,2020-06-03 22:16:18 01003DE00918E000,"The Darkside Detective",,playable,2020-06-03 22:16:18
010032B015D66000,"The DioField Chronicle",,playable,2025-09-05 11:35:50
01000A10041EA000,"The Elder Scrolls V: Skyrim",gpu;crash,ingame,2024-07-14 03:21:31 01000A10041EA000,"The Elder Scrolls V: Skyrim",gpu;crash,ingame,2024-07-14 03:21:31
01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45 01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45
0100CA100489C000,"The Escapists 2",nvdec,playable,2020-09-24 12:31:31 0100CA100489C000,"The Escapists 2",nvdec,playable,2020-09-24 12:31:31
1 title_id game_name labels status last_updated
978 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
979 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19
980 01008CB01E52E000 DOOM + DOOM II opengl;ldn-untested;LAN playable 2024-09-12 07:06:01
981 010029D00E740000 DOOM 3 crash crash;slow menus 2024-08-03 05:25:47
982 01005D700E742000 DOOM 64 nvdec;vulkan playable 2020-10-13 23:47:28
983 0100D4F00DD02000 DOOM II (Classic) nvdec;online playable 2021-06-03 20:10:01
984 0100B1A00D8CE000 DOOM® Eternal gpu;slow;nvdec;online-broken ingame 2024-08-28 15:57:17
1097 0100F9600E746000 ESP Ra.De. Psi audio;slow ingame 2024-03-07 15:05:08
1098 010073000FE18000 Esports powerful pro yakyuu 2020 gpu;crash;Needs More Attention ingame 2024-04-29 05:34:14
1099 01004F9012FD8000 Estranged: The Departure nvdec;UE4 playable 2022-10-24 10:37:58
1100 010018f01e0a0000 010018F01E0A0000 Eternights playable 2025-07-30 12:10:24
1101 0100CB900B498000 Eternum Ex playable 2021-01-13 20:28:32
1102 010092501EB2C000 Europa (Demo) gpu;crash;UE4 ingame 2024-04-23 10:47:12
1103 01007BE0160D6000 EVE ghost enemies gpu ingame 2023-01-14 03:13:30
1243 010003F00BD48000 Friday the 13th: Killer Puzzle playable 2021-01-28 01:33:38
1244 010092A00C4B6000 Friday the 13th: The Game Ultimate Slasher Edition nvdec;online-broken;UE4 playable 2022-09-06 17:33:27
1245 0100F200178F4000 FRONT MISSION 1st: Remake playable 2023-06-09 07:44:24
1246 0100c4e018a24000 0100C4E018A24000 FRONT MISSION 2: Remake playable 2025-07-30 12:11:23
1247 01007E6019872000 FRONT MISSION 3: Remake playable 2025-07-30 12:12:02
1248 0100861012474000 Frontline Zed playable 2020-10-03 12:55:59
1249 0100B5300B49A000 Frost playable 2022-07-27 12:00:36
1440 0100C2700E338000 Heroland playable 2020-08-05 15:35:39
1441 01007AC00E012000 HexaGravity playable 2021-05-28 13:47:48
1442 01004E800F03C000 Hidden slow ingame 2022-10-05 10:56:53
1443 0100C1101EE5A000 High on Life menus 2025-08-26 19:11:00
1444 0100F6A00A684000 Higurashi no Naku Koro ni Hō audio ingame 2021-09-18 14:40:28
1445 0100F8D0129F4000 Himehibi 1 gakki - Princess Days crash nothing 2021-11-03 08:34:19
1446 0100F3D008436000 Hiragana Pixel Party playable 2021-01-14 08:36:50
1450 0100F7300ED2C000 Hoggy2 playable 2022-10-10 13:53:35
1451 0100F7E00C70E000 Hogwarts Legacy UE4;slow ingame 2024-09-03 19:53:58
1452 0100633007D48000 Hollow Knight nvdec playable 2023-01-16 15:44:56
1453 010013C00E930000 Hollow Knight: Silksong playable 2025-09-04 17:23:22
1454 0100F2100061E800 Hollow0 UE4;gpu ingame 2021-03-03 23:42:56
1455 0100342009E16000 Holy Potatoes! What The Hell?! playable 2020-07-03 10:48:56
1456 010071B00C904000 HoPiKo playable 2021-01-13 20:12:38
1889 010097800EA20000 Monster Energy Supercross - The Official Videogame 3 UE4;audout;nvdec;online playable 2021-06-14 12:37:54
1890 0100E9900ED74000 Monster Farm 32-bit;nvdec playable 2021-05-05 19:29:13
1891 0100770008DD8000 Monster Hunter Generations Ultimate™ 32-bit;online-broken;ldn-works playable 2024-03-18 14:35:36
1892 0100B04011742000 Monster Hunter Rise MONSTER HUNTER RISE gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works ingame 2024-08-24 11:04:59
1893 010093A01305C000 Monster Hunter Rise Demo online-broken;ldn-works;demo playable 2022-10-18 23:04:17
1894 0100E21011446000 Monster Hunter Stories 2: Wings of Ruin services ingame 2022-07-10 19:27:30
1895 010042501329E000 MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version demo playable 2022-11-13 22:20:26
2314 010077B00BDD8000 Professional Farmer: Nintendo Switch™ Edition slow playable 2020-12-16 13:38:19
2315 010018300C83A000 Professor Lupo and his Horrible Pets playable 2020-06-12 00:08:45
2316 0100D1F0132F6000 Professor Lupo: Ocean playable 2021-04-14 16:33:33
2317 0100c3a017834000 0100C3A017834000 Prodeus playable 2025-07-30 12:07:52
2318 0100BBD00976C000 Project Highrise: Architect's Edition playable 2022-08-10 17:19:12
2319 0100ACE00DAB6000 Project Nimbus: Complete Edition nvdec;UE4;vulkan-backend-bug playable 2022-08-10 17:35:43
2320 01002980140F6000 Project TRIANGLE STRATEGY™ Debut Demo UE4;demo playable 2022-10-24 21:40:27
2580 0100C610154CA000 Shadowrun: Hong Kong - Extended Edition gpu;Needs Update ingame 2022-10-04 20:53:09
2581 010000000EEF0000 Shadows 2: Perfidia playable 2020-08-07 12:43:46
2582 0100AD700CBBE000 Shadows of Adam playable 2021-01-11 13:35:58
2583 010037A01F96C000 Shadows of the Damned: Hella Remastered playable 2025-09-05 11:34:32
2584 01002A800C064000 Shadowverse Champions Battle playable 2022-10-02 22:59:29
2585 01003B90136DA000 Shadowverse: Champion's Battle crash nothing 2023-03-06 00:31:50
2586 0100820013612000 Shady Part of Me playable 2022-10-20 11:31:55
2979 0100EBA01548E000 The Cruel King and the Great Hero gpu;services ingame 2022-12-02 07:02:08
2980 010051800E922000 The Dark Crystal: Age of Resistance Tactics playable 2020-08-11 13:43:41
2981 01003DE00918E000 The Darkside Detective playable 2020-06-03 22:16:18
2982 010032B015D66000 The DioField Chronicle playable 2025-09-05 11:35:50
2983 01000A10041EA000 The Elder Scrolls V: Skyrim gpu;crash ingame 2024-07-14 03:21:31
2984 01004A9006B84000 The End Is Nigh playable 2020-06-01 11:26:45
2985 0100CA100489C000 The Escapists 2 nvdec playable 2020-09-24 12:31:31

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -131,7 +131,6 @@ namespace Ryujinx.Common.Collections
if (parent.Predecessor != null) if (parent.Predecessor != null)
{ {
newNode.Predecessor = parent.Predecessor; newNode.Predecessor = parent.Predecessor;
parent.Predecessor = newNode;
newNode.Predecessor.Successor = newNode; newNode.Predecessor.Successor = newNode;
} }

View File

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

View File

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

View File

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

View File

@@ -2,8 +2,18 @@ namespace Ryujinx.Common
{ {
public static class SharedConstants public static class SharedConstants
{ {
public const string DefaultLanPlayHost = "ryuldn.vudjun.com"; public const string DefaultLanPlayHost = "ldn.ryujinx.app";
public const short LanPlayPort = 30456; public const short LanPlayPort = 30456;
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; 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";
} }
} }

View File

@@ -182,6 +182,7 @@ namespace Ryujinx.Common
"01001cc01b2d4000", // Goat Simulator 3 "01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2 "01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy "0100f7e00c70e000", // Hogwarts Legacy
"010013c00e930000", // Hollow Knight: Silksong
"010085500130a000", // Lego City: Undercover "010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures "010073c01af34000", // LEGO Horizon Adventures
"0100d71004694000", // Minecraft "0100d71004694000", // Minecraft

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -821,6 +821,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// on the screen scissor state, then we need to force only one texture to be bound to avoid // on the screen scissor state, then we need to force only one texture to be bound to avoid
// host clipping. // host clipping.
ScreenScissorState screenScissorState = _state.State.ScreenScissorState; ScreenScissorState screenScissorState = _state.State.ScreenScissorState;
Span<ScissorState> scissorStateSpan = _state.State.ScissorState.AsSpan();
bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0; bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0;
bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0; bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0;
@@ -831,9 +833,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool fullClear = screenScissorState.X == 0 && screenScissorState.Y == 0; bool fullClear = screenScissorState.X == 0 && screenScissorState.Y == 0;
if (fullClear && clearAffectedByScissor && _state.State.ScissorState[0].Enable) if (fullClear && clearAffectedByScissor && scissorStateSpan[0].Enable)
{ {
ref ScissorState scissorState = ref _state.State.ScissorState[0]; ref ScissorState scissorState = ref scissorStateSpan[0];
fullClear = scissorState.X1 == screenScissorState.X && fullClear = scissorState.X1 == screenScissorState.X &&
scissorState.Y1 == screenScissorState.Y && scissorState.Y1 == screenScissorState.Y &&
@@ -892,9 +894,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int scissorW = screenScissorState.Width; int scissorW = screenScissorState.Width;
int scissorH = screenScissorState.Height; int scissorH = screenScissorState.Height;
if (clearAffectedByScissor && _state.State.ScissorState[0].Enable) if (clearAffectedByScissor && scissorStateSpan[0].Enable)
{ {
ref ScissorState scissorState = ref _state.State.ScissorState[0]; ref ScissorState scissorState = ref scissorStateSpan[0];
scissorX = Math.Max(scissorX, scissorState.X1); scissorX = Math.Max(scissorX, scissorState.X1);
scissorY = Math.Max(scissorY, scissorState.Y1); scissorY = Math.Max(scissorY, scissorState.Y1);

View File

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

View File

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

View File

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

View File

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

View File

@@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong size, ulong size,
BufferStage stage, BufferStage stage,
bool sparseCompatible, bool sparseCompatible,
List<Buffer> baseBuffers) RangeItem<Buffer>[] baseBuffers)
{ {
_context = context; _context = context;
_physicalMemory = physicalMemory; _physicalMemory = physicalMemory;
@@ -128,18 +128,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
List<IRegionHandle> baseHandles = null; List<IRegionHandle> baseHandles = null;
if (baseBuffers.Count != 0) if (baseBuffers.Length != 0)
{ {
baseHandles = new List<IRegionHandle>(); baseHandles = new List<IRegionHandle>();
foreach (Buffer buffer in baseBuffers) foreach (RangeItem<Buffer> item in baseBuffers)
{ {
if (buffer._useGranular) if (item.Value._useGranular)
{ {
baseHandles.AddRange((buffer._memoryTrackingGranular.GetHandles())); baseHandles.AddRange((item.Value._memoryTrackingGranular.GetHandles()));
} }
else else
{ {
baseHandles.Add(buffer._memoryTracking); baseHandles.Add(item.Value._memoryTracking);
} }
} }
} }

View File

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

View File

@@ -81,13 +81,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
MemoryRange subRange = range.GetSubRange(index); MemoryRange subRange = range.GetSubRange(index);
_buffers.Lock.EnterReadLock(); _buffers.Lock.EnterReadLock();
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(subRange.Address, subRange.Size); Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
RangeItem<Buffer> current = first; for (int i = 0; i < overlaps.Length; i++)
while (last != null && current != last.Next)
{ {
current.Value.Unmapped(subRange.Address, subRange.Size); overlaps[i].Value.Unmapped(subRange.Address, subRange.Size);
current = current.Next;
} }
_buffers.Lock.ExitReadLock(); _buffers.Lock.ExitReadLock();
@@ -489,10 +487,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param> /// <param name="stage">The type of usage that created the buffer</param>
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage) private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
{ {
Buffer newBuffer = null;
_buffers.Lock.EnterWriteLock(); _buffers.Lock.EnterWriteLock();
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(address, size); Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
if (first is not null) if (overlaps.Length != 0)
{ {
// The buffer already exists. We can just return the existing buffer // The buffer already exists. We can just return the existing buffer
// if the buffer we need is fully contained inside the overlapping buffer. // if the buffer we need is fully contained inside the overlapping buffer.
@@ -502,7 +502,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong endAddress = address + size; ulong endAddress = address + size;
if (first.Address > address || first.EndAddress < endAddress) if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress)
{ {
bool anySparseCompatible = false; bool anySparseCompatible = false;
@@ -515,52 +515,60 @@ namespace Ryujinx.Graphics.Gpu.Memory
// sequential memory. // sequential memory.
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the // Allowing for 2 pages (rather than just one) is necessary to catch cases where the
// range crosses a page, and after alignment, ends having a size of 2 pages. // range crosses a page, and after alignment, ends having a size of 2 pages.
if (first == last && if (overlaps.Length == 1 &&
address >= first.Address && address >= overlaps[0].Address &&
endAddress - first.EndAddress <= BufferAlignmentSize * 2) endAddress - overlaps[0].EndAddress <= BufferAlignmentSize * 2)
{ {
// Try to grow the buffer by 1.5x of its current size. // Try to grow the buffer by 1.5x of its current size.
// This improves performance in the cases where the buffer is resized often by small amounts. // This improves performance in the cases where the buffer is resized often by small amounts.
ulong existingSize = first.Value.Size; ulong existingSize = overlaps[0].Value.Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask; ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize); size = Math.Max(size, growthSize);
endAddress = address + size; endAddress = address + size;
(first, last) = _buffers.FindOverlaps(address, size); overlaps = _buffers.FindOverlapsAsSpan(address, size);
} }
address = Math.Min(address, first.Address); address = Math.Min(address, overlaps[0].Address);
endAddress = Math.Max(endAddress, last.EndAddress); endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
List<Buffer> overlaps = [];
RangeItem<Buffer> current = first; for (int i = 0; i < overlaps.Length; i++)
while (current != last.Next)
{ {
anySparseCompatible |= current.Value.SparseCompatible; anySparseCompatible |= overlaps[i].Value.SparseCompatible;
overlaps.Add(current.Value);
_buffers.Remove(current.Value);
current = current.Next;
} }
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
_buffers.Lock.ExitWriteLock();
ulong newSize = endAddress - address; ulong newSize = endAddress - address;
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlaps); newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray);
}
_buffers.Add(newBuffer); else
{
_buffers.Lock.ExitWriteLock();
} }
} }
else else
{ {
_buffers.Lock.ExitWriteLock();
// No overlap, just create a new buffer. // No overlap, just create a new buffer.
Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []); newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []);
}
_buffers.Add(buffer);
if (newBuffer is not null)
{
_buffers.Lock.EnterWriteLock();
_buffers.Add(newBuffer);
_buffers.Lock.ExitWriteLock();
} }
_buffers.Lock.ExitWriteLock();
} }
/// <summary> /// <summary>
@@ -575,67 +583,74 @@ namespace Ryujinx.Graphics.Gpu.Memory
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment) private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment)
{ {
bool sparseAligned = alignment >= SparseBufferAlignmentSize; bool sparseAligned = alignment >= SparseBufferAlignmentSize;
Buffer newBuffer = null;
_buffers.Lock.EnterWriteLock(); _buffers.Lock.EnterWriteLock();
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(address, size); Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
if (first is not null) if (overlaps.Length != 0)
{ {
// If the buffer already exists, make sure if covers the entire range, // If the buffer already exists, make sure if covers the entire range,
// and make sure it is properly aligned, otherwise sparse mapping may fail. // and make sure it is properly aligned, otherwise sparse mapping may fail.
ulong endAddress = address + size; ulong endAddress = address + size;
if (first.Address > address || if (overlaps[0].Address > address ||
first.EndAddress < endAddress || overlaps[0].EndAddress < endAddress ||
(first.Address & (alignment - 1)) != 0 || (overlaps[0].Address & (alignment - 1)) != 0 ||
(!first.Value.SparseCompatible && sparseAligned)) (!overlaps[0].Value.SparseCompatible && sparseAligned))
{ {
// We need to make sure the new buffer is properly aligned. // We need to make sure the new buffer is properly aligned.
// However, after the range is aligned, it is possible that it // However, after the range is aligned, it is possible that it
// overlaps more buffers, so try again after each extension // overlaps more buffers, so try again after each extension
// and ensure we cover all overlaps. // and ensure we cover all overlaps.
RangeItem<Buffer> oldFirst; endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
endAddress = Math.Max(endAddress, last.EndAddress); int oldOverlapCount;
do do
{ {
address = Math.Min(address, first.Address); address = Math.Min(address, overlaps[0].Address);
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
address &= ~(alignment - 1); address &= ~(alignment - 1);
oldFirst = first; oldOverlapCount = overlaps.Length;
(first, last) = _buffers.FindOverlaps(address, endAddress - address); overlaps = _buffers.FindOverlapsAsSpan(address, endAddress - address);
} }
while (oldFirst != first); while (oldOverlapCount != overlaps.Length);
ulong newSize = endAddress - address; ulong newSize = endAddress - address;
List<Buffer> overlaps = []; RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
RangeItem<Buffer> current = first; _buffers.RemoveRange(overlaps[0], overlaps[^1]);
while (current != last.Next)
{
overlaps.Add(current.Value);
_buffers.Remove(current.Value);
current = current.Next;
}
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlaps); _buffers.Lock.ExitWriteLock();
_buffers.Add(newBuffer); newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray);
}
else
{
_buffers.Lock.ExitWriteLock();
} }
} }
else else
{ {
_buffers.Lock.ExitWriteLock();
// No overlap, just create a new buffer. // No overlap, just create a new buffer.
Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []); newBuffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []);
}
_buffers.Add(buffer);
if (newBuffer is not null)
{
_buffers.Lock.EnterWriteLock();
_buffers.Add(newBuffer);
_buffers.Lock.ExitWriteLock();
} }
_buffers.Lock.ExitWriteLock();
} }
/// <summary> /// <summary>
@@ -648,13 +663,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">The type of usage that created the buffer</param> /// <param name="stage">The type of usage that created the buffer</param>
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param> /// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
/// <param name="overlaps">Buffers overlapping the range</param> /// <param name="overlaps">Buffers overlapping the range</param>
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, List<Buffer> overlaps) private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, RangeItem<Buffer>[] overlaps)
{ {
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps); Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps);
for (int index = 0; index < overlaps.Count; index++) for (int index = 0; index < overlaps.Length; index++)
{ {
Buffer buffer = overlaps[index]; Buffer buffer = overlaps[index].Value;
int dstOffset = (int)(buffer.Address - newBuffer.Address); int dstOffset = (int)(buffer.Address - newBuffer.Address);
@@ -882,7 +897,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
MemoryRange subRange = range.GetSubRange(i); MemoryRange subRange = range.GetSubRange(i);
Buffer subBuffer = _buffers.FindOverlapFast(subRange.Address, subRange.Size).Value; Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value;
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size); subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
@@ -930,7 +945,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (size != 0) if (size != 0)
{ {
buffer = _buffers.FindOverlapFast(address, size).Value; buffer = _buffers.FindOverlap(address, size).Value;
buffer.CopyFromDependantVirtualBuffers(); buffer.CopyFromDependantVirtualBuffers();
buffer.SynchronizeMemory(address, size); buffer.SynchronizeMemory(address, size);
@@ -980,7 +995,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (size != 0) if (size != 0)
{ {
Buffer buffer = _buffers.FindOverlapFast(address, size).Value; Buffer buffer = _buffers.FindOverlap(address, size).Value;
if (copyBackVirtual) if (copyBackVirtual)
{ {

View File

@@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
class BufferModifiedRangeList : NonOverlappingRangeList<BufferModifiedRange> class BufferModifiedRangeList : NonOverlappingRangeList<BufferModifiedRange>
{ {
private new const int BackingInitialSize = 8; private const int BackingInitialSize = 8;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly Buffer _parent; private readonly Buffer _parent;
@@ -117,18 +117,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void ExcludeModifiedRegions(ulong address, ulong size, Action<ulong, ulong> action) public void ExcludeModifiedRegions(ulong address, ulong size, Action<ulong, ulong> action)
{ {
// Slices a given region using the modified regions in the list. Calls the action for the new slices. // Slices a given region using the modified regions in the list. Calls the action for the new slices.
bool lockOwner = Lock.IsReadLockHeld; Lock.EnterReadLock();
if (!lockOwner)
{
Lock.EnterReadLock();
}
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size); Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
RangeItem<BufferModifiedRange> current = first; for (int i = 0; i < overlaps.Length; i++)
while (last != null && current != last.Next)
{ {
BufferModifiedRange overlap = current.Value; BufferModifiedRange overlap = overlaps[i].Value;
if (overlap.Address > address) if (overlap.Address > address)
{ {
@@ -139,13 +134,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Remaining region is after this overlap. // Remaining region is after this overlap.
size -= overlap.EndAddress - address; size -= overlap.EndAddress - address;
address = overlap.EndAddress; address = overlap.EndAddress;
current = current.Next;
} }
if (!lockOwner) Lock.ExitReadLock();
{
Lock.ExitReadLock();
}
if ((long)size > 0) if ((long)size > 0)
{ {
@@ -162,12 +153,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size of the modified region in bytes</param> /// <param name="size">Size of the modified region in bytes</param>
public void SignalModified(ulong address, ulong size) public void SignalModified(ulong address, ulong size)
{ {
// 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) = FindOverlaps(address, size);
ulong endAddress = address + size; ulong endAddress = address + size;
ulong syncNumber = _context.SyncNumber; ulong syncNumber = _context.SyncNumber;
// We may overlap with some existing modified regions. They must be cut into by the new entry.
Lock.EnterWriteLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null) if (first is null)
{ {
@@ -176,10 +166,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return; return;
} }
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first == last) if (first == last)
{ {
if (first.Address == address && first.EndAddress == endAddress) if (first.Address == address && first.EndAddress == endAddress)
@@ -193,14 +179,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (first.Address < address) if (first.Address < address)
{ {
first.Value.Size = address - first.Address; first.Value.Size = address - first.Address;
Update(first);
extendsPre = true;
if (first.EndAddress > endAddress) if (first.EndAddress > endAddress)
{ {
buffPost = new BufferModifiedRange(endAddress, first.EndAddress - endAddress, Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.Value.SyncNumber, first.Value.Parent); first.Value.SyncNumber, first.Value.Parent));
extendsPost = true;
} }
} }
else else
@@ -209,6 +193,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
first.Value.Size = first.EndAddress - endAddress; first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress; first.Value.Address = endAddress;
Update(first);
} }
else else
{ {
@@ -216,11 +201,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
if (extendsPre && extendsPost)
{
Add(buffPost);
}
Add(new BufferModifiedRange(address, size, syncNumber, this)); Add(new BufferModifiedRange(address, size, syncNumber, this));
Lock.ExitWriteLock(); Lock.ExitWriteLock();
@@ -228,6 +208,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
BufferModifiedRange buffPre = null; BufferModifiedRange buffPre = null;
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address) if (first.Address < address)
{ {
@@ -269,19 +252,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction) public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
{ {
Lock.EnterReadLock(); Lock.EnterReadLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size); Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
RangeItem<BufferModifiedRange> current = first; for (int i = 0; i < overlaps.Length; i++)
while (last != null && current != last.Next)
{ {
BufferModifiedRange overlap = current.Value; BufferModifiedRange overlap = overlaps[i].Value;
if (overlap.SyncNumber == syncNumber) if (overlap.SyncNumber == syncNumber)
{ {
rangeAction(overlap.Address, overlap.Size); rangeAction(overlap.Address, overlap.Size);
} }
current = current.Next;
} }
Lock.ExitReadLock(); Lock.ExitReadLock();
@@ -295,21 +275,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="rangeAction">The action to call for each modified range</param> /// <param name="rangeAction">The action to call for each modified range</param>
public void GetRanges(ulong address, ulong size, Action<ulong, ulong> rangeAction) public void GetRanges(ulong address, ulong size, Action<ulong, ulong> rangeAction)
{ {
List<RangeItem<BufferModifiedRange>> overlaps = [];
// We use the non-span method here because keeping the lock will cause a deadlock. // We use the non-span method here because keeping the lock will cause a deadlock.
Lock.EnterReadLock(); Lock.EnterReadLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size); RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
RangeItem<BufferModifiedRange> current = first;
while (last != null && current != last.Next)
{
overlaps.Add(current);
current = current.Next;
}
Lock.ExitReadLock(); Lock.ExitReadLock();
for (int i = 0; i < overlaps.Count; i++) for (int i = 0; i < overlaps.Length; i++)
{ {
BufferModifiedRange overlap = overlaps[i].Value; BufferModifiedRange overlap = overlaps[i].Value;
rangeAction(overlap.Address, overlap.Size); rangeAction(overlap.Address, overlap.Size);
@@ -325,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
public bool HasRange(ulong address, ulong size) public bool HasRange(ulong address, ulong size)
{ {
Lock.EnterReadLock(); Lock.EnterReadLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> _) = FindOverlaps(address, size); RangeItem<BufferModifiedRange> first = FindOverlapFast(address, size);
bool result = first is not null; bool result = first is not null;
Lock.ExitReadLock(); Lock.ExitReadLock();
return result; return result;
@@ -382,9 +353,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong clampAddress = Math.Max(address, overlap.Address); ulong clampAddress = Math.Max(address, overlap.Address);
ulong clampEnd = Math.Min(endAddress, overlap.EndAddress); ulong clampEnd = Math.Min(endAddress, overlap.EndAddress);
Lock.EnterWriteLock();
ClearPart(overlap, clampAddress, clampEnd); ClearPart(overlap, clampAddress, clampEnd);
Lock.ExitWriteLock();
RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction); RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction);
} }
@@ -414,40 +383,24 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong endAddress = address + size; ulong endAddress = address + size;
ulong currentSync = _context.SyncNumber; ulong currentSync = _context.SyncNumber;
int rangeCount = 0;
List<RangeItem<BufferModifiedRange>> overlaps = [];
// Range list must be consistent for this operation // Range list must be consistent for this operation
Lock.EnterReadLock();
if (_migrationTarget != null) if (_migrationTarget != null)
{
rangeCount = -1;
}
else
{
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
RangeItem<BufferModifiedRange> current = first;
while (last != null && current != last.Next)
{
rangeCount++;
overlaps.Add(current);
current = current.Next;
}
}
Lock.ExitReadLock();
if (rangeCount == -1)
{ {
_migrationTarget!.WaitForAndFlushRanges(address, size); _migrationTarget!.WaitForAndFlushRanges(address, size);
return; 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) if (rangeCount == 0)
{ {
Lock.ExitWriteLock();
return; return;
} }
@@ -470,6 +423,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (highestDiff == long.MinValue) if (highestDiff == long.MinValue)
{ {
Lock.ExitWriteLock();
return; return;
} }
@@ -477,6 +432,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff); _context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress); RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress);
Lock.ExitWriteLock();
} }
/// <summary> /// <summary>
@@ -516,6 +473,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ranges._migrationTarget = this; ranges._migrationTarget = this;
Lock.EnterWriteLock(); Lock.EnterWriteLock();
foreach (BufferModifiedRange range in inheritRanges) foreach (BufferModifiedRange range in inheritRanges)
{ {
Add(range); Add(range);
@@ -595,7 +553,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
ulong endAddress = address + size; ulong endAddress = address + size;
Lock.EnterWriteLock(); Lock.EnterWriteLock();
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size); (RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
if (first is null) if (first is null)
{ {
@@ -603,22 +561,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
return; return;
} }
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first == last) if (first == last)
{ {
if (first.Address < address) if (first.Address < address)
{ {
first.Value.Size = address - first.Address; first.Value.Size = address - first.Address;
extendsPre = true; Update(first);
if (first.EndAddress > endAddress) if (first.EndAddress > endAddress)
{ {
buffPost = new BufferModifiedRange(endAddress, first.EndAddress - endAddress, Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress,
first.Value.SyncNumber, first.Value.Parent); first.Value.SyncNumber, first.Value.Parent));
extendsPost = true;
} }
} }
else else
@@ -627,6 +580,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
first.Value.Size = first.EndAddress - endAddress; first.Value.Size = first.EndAddress - endAddress;
first.Value.Address = endAddress; first.Value.Address = endAddress;
Update(first);
} }
else else
{ {
@@ -634,16 +588,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
if (extendsPre && extendsPost)
{
Add(buffPost);
}
Lock.ExitWriteLock(); Lock.ExitWriteLock();
return; return;
} }
BufferModifiedRange buffPre = null; BufferModifiedRange buffPre = null;
BufferModifiedRange buffPost = null;
bool extendsPost = false;
bool extendsPre = false;
if (first.Address < address) if (first.Address < address)
{ {

View File

@@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStage FromUsage(BufferUsageFlags flags) public static BufferStage FromUsage(BufferUsageFlags flags)
{ {
if (flags.HasFlag(BufferUsageFlags.Write)) if ((flags & BufferUsageFlags.Write) == BufferUsageFlags.Write)
{ {
return BufferStage.StorageWrite; return BufferStage.StorageWrite;
} }
@@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStage FromUsage(TextureUsageFlags flags) public static BufferStage FromUsage(TextureUsageFlags flags)
{ {
if (flags.HasFlag(TextureUsageFlags.ImageStore)) if ((flags & TextureUsageFlags.ImageStore) == TextureUsageFlags.ImageStore)
{ {
return BufferStage.StorageWrite; return BufferStage.StorageWrite;
} }

View File

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

View File

@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong originalVa = gpuVa; ulong originalVa = gpuVa;
_virtualRanges.Lock.EnterWriteLock(); _virtualRanges.Lock.EnterWriteLock();
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlaps(gpuVa, size); (RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
if (first is not null) if (first is not null)
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types namespace Ryujinx.Graphics.Nvdec.Vp9.Types
@@ -65,36 +66,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Mv MvPredQ4(int idx) public Mv MvPredQ4(int idx)
{ {
Span<BModeInfo> bmiSpan = Bmi.AsSpan();
Mv res = new() Mv res = new()
{ {
Row = (short)ReconInter.RoundMvCompQ4( Row = (short)ReconInter.RoundMvCompQ4(
Bmi[0].Mv[idx].Row + Bmi[1].Mv[idx].Row + bmiSpan[0].Mv[idx].Row + bmiSpan[1].Mv[idx].Row +
Bmi[2].Mv[idx].Row + Bmi[3].Mv[idx].Row), bmiSpan[2].Mv[idx].Row + bmiSpan[3].Mv[idx].Row),
Col = (short)ReconInter.RoundMvCompQ4( Col = (short)ReconInter.RoundMvCompQ4(
Bmi[0].Mv[idx].Col + Bmi[1].Mv[idx].Col + bmiSpan[0].Mv[idx].Col + bmiSpan[1].Mv[idx].Col +
Bmi[2].Mv[idx].Col + Bmi[3].Mv[idx].Col) bmiSpan[2].Mv[idx].Col + bmiSpan[3].Mv[idx].Col)
}; };
return res; return res;
} }
public Mv MvPredQ2(int idx, int block0, int block1) public Mv MvPredQ2(int idx, int block0, int block1)
{ {
Span<BModeInfo> bmiSpan = Bmi.AsSpan();
Mv res = new() Mv res = new()
{ {
Row = (short)ReconInter.RoundMvCompQ2( Row = (short)ReconInter.RoundMvCompQ2(
Bmi[block0].Mv[idx].Row + bmiSpan[block0].Mv[idx].Row +
Bmi[block1].Mv[idx].Row), bmiSpan[block1].Mv[idx].Row),
Col = (short)ReconInter.RoundMvCompQ2( Col = (short)ReconInter.RoundMvCompQ2(
Bmi[block0].Mv[idx].Col + bmiSpan[block0].Mv[idx].Col +
Bmi[block1].Mv[idx].Col) bmiSpan[block1].Mv[idx].Col)
}; };
return res; return res;
} }
// Performs mv sign inversion if indicated by the reference frame combination. // Performs mv sign inversion if indicated by the reference frame combination.
public Mv ScaleMv(int refr, sbyte thisRefFrame, ref Array4<sbyte> refSignBias) public Mv ScaleMv(int refr, sbyte thisRefFrame, Span<sbyte> refSignBias)
{ {
Mv mv = Mv[refr]; Mv mv = Mv[refr];
if (refSignBias[RefFrame[refr]] != refSignBias[thisRefFrame]) if (refSignBias[RefFrame[refr]] != refSignBias[thisRefFrame])
{ {
mv.Row *= -1; mv.Row *= -1;

View File

@@ -99,10 +99,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
} }
else else
{ {
Span<Array2<uint>> bitsSpan = counts.Bits[comp].AsSpan();
int b = c + Constants.Class0Bits - 1; // Number of bits int b = c + Constants.Class0Bits - 1; // Number of bits
for (int i = 0; i < b; ++i) for (int i = 0; i < b; ++i)
{ {
counts.Bits[comp][i][(d >> i) & 1] += (uint)incr; bitsSpan[i][(d >> i) & 1] += (uint)incr;
} }
counts.Fp[comp][f] += (uint)incr; counts.Fp[comp][f] += (uint)incr;

View File

@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Array8<uint> FeatureMask; public Array8<uint> FeatureMask;
public int AqAvOffset; public int AqAvOffset;
public static byte GetPredProbSegId(ref Array3<byte> segPredProbs, ref MacroBlockD xd) public static byte GetPredProbSegId(ReadOnlySpan<byte> segPredProbs, ref MacroBlockD xd)
{ {
return segPredProbs[xd.GetPredContextSegId()]; return segPredProbs[xd.GetPredContextSegId()];
} }
@@ -105,9 +105,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
UpdateMap = rb.ReadBit() != 0; UpdateMap = rb.ReadBit() != 0;
if (UpdateMap) if (UpdateMap)
{ {
Span<byte> segTreeProbSpan = fc.SegTreeProb.AsSpan();
Span<byte> segPredProbSpan = fc.SegPredProb.AsSpan();
for (int i = 0; i < SegTreeProbs; i++) for (int i = 0; i < SegTreeProbs; i++)
{ {
fc.SegTreeProb[i] = rb.ReadBit() != 0 segTreeProbSpan[i] = rb.ReadBit() != 0
? (byte)rb.ReadLiteral(8) ? (byte)rb.ReadLiteral(8)
: (byte)Prob.MaxProb; : (byte)Prob.MaxProb;
} }
@@ -117,7 +120,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{ {
for (int i = 0; i < PredictionProbs; i++) for (int i = 0; i < PredictionProbs; i++)
{ {
fc.SegPredProb[i] = rb.ReadBit() != 0 segPredProbSpan[i] = rb.ReadBit() != 0
? (byte)rb.ReadLiteral(8) ? (byte)rb.ReadLiteral(8)
: (byte)Prob.MaxProb; : (byte)Prob.MaxProb;
} }
@@ -126,7 +129,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{ {
for (int i = 0; i < PredictionProbs; i++) for (int i = 0; i < PredictionProbs; i++)
{ {
fc.SegPredProb[i] = Prob.MaxProb; segPredProbSpan[i] = Prob.MaxProb;
} }
} }
} }

View File

@@ -155,9 +155,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public bool CompoundReferenceAllowed() public bool CompoundReferenceAllowed()
{ {
Span<sbyte> refFrameSignBiasSpan = RefFrameSignBias.AsSpan();
for (int i = 1; i < Constants.RefsPerFrame; ++i) for (int i = 1; i < Constants.RefsPerFrame; ++i)
{ {
if (RefFrameSignBias[i + 1] != RefFrameSignBias[1]) if (refFrameSignBiasSpan[i + 1] != refFrameSignBiasSpan[1])
{ {
return true; return true;
} }
@@ -173,13 +175,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public readonly int GetFreeFb() public readonly int GetFreeFb()
{ {
ref Array12<RefCntBuffer> frameBufs = ref BufferPool.Value.FrameBufs; Span<RefCntBuffer> frameBuffs = BufferPool.Value.FrameBufs.AsSpan();
int i; int i;
for (i = 0; i < Constants.FrameBuffers; ++i) for (i = 0; i < Constants.FrameBuffers; ++i)
{ {
if (frameBufs[i].RefCount == 0) if (frameBuffs[i].RefCount == 0)
{ {
break; break;
} }
@@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
if (i != Constants.FrameBuffers) if (i != Constants.FrameBuffers)
{ {
frameBufs[i].RefCount = 1; frameBuffs[i].RefCount = 1;
} }
else else
{ {
@@ -240,25 +242,29 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
private void AllocSegMap(MemoryAllocator allocator, int segMapSize) private void AllocSegMap(MemoryAllocator allocator, int segMapSize)
{ {
Span<ArrayPtr<byte>> segMapArraySpan = SegMapArray.AsSpan();
for (int i = 0; i < Constants.NumPingPongBuffers; ++i) for (int i = 0; i < Constants.NumPingPongBuffers; ++i)
{ {
SegMapArray[i] = allocator.Allocate<byte>(segMapSize); segMapArraySpan[i] = allocator.Allocate<byte>(segMapSize);
} }
// Init the index. // Init the index.
SegMapIdx = 0; SegMapIdx = 0;
PrevSegMapIdx = 1; PrevSegMapIdx = 1;
CurrentFrameSegMap = SegMapArray[SegMapIdx]; CurrentFrameSegMap = segMapArraySpan[SegMapIdx];
LastFrameSegMap = SegMapArray[PrevSegMapIdx]; LastFrameSegMap = segMapArraySpan[PrevSegMapIdx];
} }
private void FreeSegMap(MemoryAllocator allocator) private void FreeSegMap(MemoryAllocator allocator)
{ {
Span<ArrayPtr<byte>> segMapArraySpan = SegMapArray.AsSpan();
for (int i = 0; i < Constants.NumPingPongBuffers; ++i) for (int i = 0; i < Constants.NumPingPongBuffers; ++i)
{ {
allocator.Free(SegMapArray[i]); allocator.Free(segMapArraySpan[i]);
SegMapArray[i] = ArrayPtr<byte>.Null; segMapArraySpan[i] = ArrayPtr<byte>.Null;
} }
CurrentFrameSegMap = ArrayPtr<byte>.Null; CurrentFrameSegMap = ArrayPtr<byte>.Null;
@@ -366,18 +372,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
internal void InitMacroBlockD(ref MacroBlockD xd, ArrayPtr<int> dqcoeff) internal void InitMacroBlockD(ref MacroBlockD xd, ArrayPtr<int> dqcoeff)
{ {
Span<MacroBlockDPlane> planeSpan = xd.Plane.AsSpan();
Span<ArrayPtr<sbyte>> aboveContextSpan = xd.AboveContext.AsSpan();
for (int i = 0; i < Constants.MaxMbPlane; ++i) for (int i = 0; i < Constants.MaxMbPlane; ++i)
{ {
xd.Plane[i].DqCoeff = dqcoeff; planeSpan[i].DqCoeff = dqcoeff;
xd.AboveContext[i] = AboveContext.Slice(i * 2 * TileInfo.MiColsAlignedToSb(MiCols)); aboveContextSpan[i] = AboveContext.Slice(i * 2 * TileInfo.MiColsAlignedToSb(MiCols));
if (i == 0) if (i == 0)
{ {
MemoryUtil.Copy(ref xd.Plane[i].SegDequant, ref YDequant); MemoryUtil.Copy(ref planeSpan[i].SegDequant, ref YDequant);
} }
else else
{ {
MemoryUtil.Copy(ref xd.Plane[i].SegDequant, ref UvDequant); MemoryUtil.Copy(ref planeSpan[i].SegDequant, ref UvDequant);
} }
xd.Fc = new Ptr<Vp9EntropyProbs>(ref Fc.Value); xd.Fc = new Ptr<Vp9EntropyProbs>(ref Fc.Value);
@@ -395,32 +404,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// Build y/uv dequant values based on segmentation. // Build y/uv dequant values based on segmentation.
if (Seg.Enabled) if (Seg.Enabled)
{ {
Span<Array2<short>> yDequantSpan1 = YDequant.AsSpan();
Span<Array2<short>> uvDequantSpan1 = UvDequant.AsSpan();
for (int i = 0; i < Constants.MaxSegments; ++i) for (int i = 0; i < Constants.MaxSegments; ++i)
{ {
Span<short> yDequantSpan2 = yDequantSpan1[i].AsSpan();
Span<short> uvDequantSpan2 = uvDequantSpan1[i].AsSpan();
int qindex = Seg.GetQIndex(i, BaseQindex); int qindex = Seg.GetQIndex(i, BaseQindex);
YDequant[i][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); yDequantSpan2[0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth);
YDequant[i][1] = QuantCommon.AcQuant(qindex, 0, BitDepth); yDequantSpan2[1] = QuantCommon.AcQuant(qindex, 0, BitDepth);
UvDequant[i][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); uvDequantSpan2[0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth);
UvDequant[i][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); uvDequantSpan2[1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth);
} }
} }
else else
{ {
Span<short> yDequantSpan = YDequant[0].AsSpan();
Span<short> uvDequantSpan = UvDequant[0].AsSpan();
int qindex = BaseQindex; int qindex = BaseQindex;
// When segmentation is disabled, only the first value is used. The // When segmentation is disabled, only the first value is used. The
// remaining are don't cares. // remaining are don't cares.
YDequant[0][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); yDequantSpan[0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth);
YDequant[0][1] = QuantCommon.AcQuant(qindex, 0, BitDepth); yDequantSpan[1] = QuantCommon.AcQuant(qindex, 0, BitDepth);
UvDequant[0][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); uvDequantSpan[0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth);
UvDequant[0][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); uvDequantSpan[1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth);
} }
} }
public void SetupScaleFactors() public void SetupScaleFactors()
{ {
Span<RefBuffer> frameRefsSpan = FrameRefs.AsSpan();
for (int i = 0; i < Constants.RefsPerFrame; ++i) for (int i = 0; i < Constants.RefsPerFrame; ++i)
{ {
ref RefBuffer refBuf = ref FrameRefs[i]; ref RefBuffer refBuf = ref frameRefsSpan[i];
refBuf.Sf.SetupScaleFactorsForFrame(refBuf.Buf.Width, refBuf.Buf.Height, Width, Height); refBuf.Sf.SetupScaleFactorsForFrame(refBuf.Buf.Width, refBuf.Buf.Height, Width, Height);
} }
} }
@@ -431,26 +451,34 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
if (ReferenceMode == ReferenceMode.Select) if (ReferenceMode == ReferenceMode.Select)
{ {
Span<byte> compInterProbSpan = fc.CompInterProb.AsSpan();
for (int i = 0; i < Constants.CompInterContexts; ++i) for (int i = 0; i < Constants.CompInterContexts; ++i)
{ {
r.DiffUpdateProb(ref fc.CompInterProb[i]); r.DiffUpdateProb(ref compInterProbSpan[i]);
} }
} }
if (ReferenceMode != ReferenceMode.Compound) if (ReferenceMode != ReferenceMode.Compound)
{ {
Span<Array2<byte>> singleRefProbSpan1 = fc.SingleRefProb.AsSpan();
for (int i = 0; i < Constants.RefContexts; ++i) for (int i = 0; i < Constants.RefContexts; ++i)
{ {
r.DiffUpdateProb(ref fc.SingleRefProb[i][0]); Span<byte> singleRefProbSpan2 = singleRefProbSpan1[i].AsSpan();
r.DiffUpdateProb(ref fc.SingleRefProb[i][1]);
r.DiffUpdateProb(ref singleRefProbSpan2[0]);
r.DiffUpdateProb(ref singleRefProbSpan2[1]);
} }
} }
if (ReferenceMode != ReferenceMode.Single) if (ReferenceMode != ReferenceMode.Single)
{ {
Span<byte> compRefProbSpan = fc.CompRefProb.AsSpan();
for (int i = 0; i < Constants.RefContexts; ++i) for (int i = 0; i < Constants.RefContexts; ++i)
{ {
r.DiffUpdateProb(ref fc.CompRefProb[i]); r.DiffUpdateProb(ref compRefProbSpan[i]);
} }
} }
} }
@@ -469,99 +497,124 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public void SetupCompoundReferenceMode() public void SetupCompoundReferenceMode()
{ {
if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.GoldenFrame]) Span<sbyte> refFrameSignBiasSpan = RefFrameSignBias.AsSpan();
Span<sbyte> compVarRefSpan = CompVarRef.AsSpan();
if (refFrameSignBiasSpan[Constants.LastFrame] == refFrameSignBiasSpan[Constants.GoldenFrame])
{ {
CompFixedRef = Constants.AltRefFrame; CompFixedRef = Constants.AltRefFrame;
CompVarRef[0] = Constants.LastFrame; compVarRefSpan[0] = Constants.LastFrame;
CompVarRef[1] = Constants.GoldenFrame; compVarRefSpan[1] = Constants.GoldenFrame;
} }
else if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.AltRefFrame]) else if (refFrameSignBiasSpan[Constants.LastFrame] == refFrameSignBiasSpan[Constants.AltRefFrame])
{ {
CompFixedRef = Constants.GoldenFrame; CompFixedRef = Constants.GoldenFrame;
CompVarRef[0] = Constants.LastFrame; compVarRefSpan[0] = Constants.LastFrame;
CompVarRef[1] = Constants.AltRefFrame; compVarRefSpan[1] = Constants.AltRefFrame;
} }
else else
{ {
CompFixedRef = Constants.LastFrame; CompFixedRef = Constants.LastFrame;
CompVarRef[0] = Constants.GoldenFrame; compVarRefSpan[0] = Constants.GoldenFrame;
CompVarRef[1] = Constants.AltRefFrame; compVarRefSpan[1] = Constants.AltRefFrame;
} }
} }
public readonly void InitMvProbs() public readonly void InitMvProbs()
{ {
Fc.Value.Joints[0] = 32; Span<byte> jointsSpan = Fc.Value.Joints.AsSpan();
Fc.Value.Joints[1] = 64; Span<byte> signSpan = Fc.Value.Sign.AsSpan();
Fc.Value.Joints[2] = 96; Span<Array10<byte>> classesSpan = Fc.Value.Classes.AsSpan();
Span<byte> classes0Span = classesSpan[0].AsSpan();
Span<byte> classes1Span = classesSpan[1].AsSpan();
Span<Array1<byte>> class0Span = Fc.Value.Class0.AsSpan();
Span<Array10<byte>> bitsSpan = Fc.Value.Bits.AsSpan();
Span<byte> bits0Span = bitsSpan[0].AsSpan();
Span<byte> bits1Span = bitsSpan[1].AsSpan();
Span<Array2<Array3<byte>>> class0FpSpan = Fc.Value.Class0Fp.AsSpan();
Span<Array3<byte>> class0Fp0Span = class0FpSpan[0].AsSpan();
Span<Array3<byte>> class0Fp1Span = class0FpSpan[1].AsSpan();
Span<byte> class0Fp00Span = class0Fp0Span[0].AsSpan();
Span<byte> class0Fp01Span = class0Fp0Span[1].AsSpan();
Span<byte> class0Fp10Span = class0Fp1Span[0].AsSpan();
Span<byte> class0Fp11Span = class0Fp1Span[1].AsSpan();
Span<Array3<byte>> fpSpan = Fc.Value.Fp.AsSpan();
Span<byte> fp0Span = fpSpan[0].AsSpan();
Span<byte> fp1Span = fpSpan[1].AsSpan();
Span<byte> class0HpSpan = Fc.Value.Class0Hp.AsSpan();
Span<byte> hpSpan = Fc.Value.Hp.AsSpan();
jointsSpan[0] = 32;
jointsSpan[1] = 64;
jointsSpan[2] = 96;
Fc.Value.Sign[0] = 128; signSpan[0] = 128;
Fc.Value.Classes[0][0] = 224; classes0Span[0] = 224;
Fc.Value.Classes[0][1] = 144; classes0Span[1] = 144;
Fc.Value.Classes[0][2] = 192; classes0Span[2] = 192;
Fc.Value.Classes[0][3] = 168; classes0Span[3] = 168;
Fc.Value.Classes[0][4] = 192; classes0Span[4] = 192;
Fc.Value.Classes[0][5] = 176; classes0Span[5] = 176;
Fc.Value.Classes[0][6] = 192; classes0Span[6] = 192;
Fc.Value.Classes[0][7] = 198; classes0Span[7] = 198;
Fc.Value.Classes[0][8] = 198; classes0Span[8] = 198;
Fc.Value.Classes[0][9] = 245; classes0Span[9] = 245;
Fc.Value.Class0[0][0] = 216; class0Span[0][0] = 216;
Fc.Value.Bits[0][0] = 136; bits0Span[0] = 136;
Fc.Value.Bits[0][1] = 140; bits0Span[1] = 140;
Fc.Value.Bits[0][2] = 148; bits0Span[2] = 148;
Fc.Value.Bits[0][3] = 160; bits0Span[3] = 160;
Fc.Value.Bits[0][4] = 176; bits0Span[4] = 176;
Fc.Value.Bits[0][5] = 192; bits0Span[5] = 192;
Fc.Value.Bits[0][6] = 224; bits0Span[6] = 224;
Fc.Value.Bits[0][7] = 234; bits0Span[7] = 234;
Fc.Value.Bits[0][8] = 234; bits0Span[8] = 234;
Fc.Value.Bits[0][9] = 240; bits0Span[9] = 240;
Fc.Value.Class0Fp[0][0][0] = 128; class0Fp00Span[0] = 128;
Fc.Value.Class0Fp[0][0][1] = 128; class0Fp00Span[1] = 128;
Fc.Value.Class0Fp[0][0][2] = 64; class0Fp00Span[2] = 64;
Fc.Value.Class0Fp[0][1][0] = 96; class0Fp01Span[0] = 96;
Fc.Value.Class0Fp[0][1][1] = 112; class0Fp01Span[1] = 112;
Fc.Value.Class0Fp[0][1][2] = 64; class0Fp01Span[2] = 64;
Fc.Value.Fp[0][0] = 64; fp0Span[0] = 64;
Fc.Value.Fp[0][1] = 96; fp0Span[1] = 96;
Fc.Value.Fp[0][2] = 64; fp0Span[2] = 64;
Fc.Value.Class0Hp[0] = 160; class0HpSpan[0] = 160;
Fc.Value.Hp[0] = 128; hpSpan[0] = 128;
Fc.Value.Sign[1] = 128; signSpan[1] = 128;
Fc.Value.Classes[1][0] = 216; classes1Span[0] = 216;
Fc.Value.Classes[1][1] = 128; classes1Span[1] = 128;
Fc.Value.Classes[1][2] = 176; classes1Span[2] = 176;
Fc.Value.Classes[1][3] = 160; classes1Span[3] = 160;
Fc.Value.Classes[1][4] = 176; classes1Span[4] = 176;
Fc.Value.Classes[1][5] = 176; classes1Span[5] = 176;
Fc.Value.Classes[1][6] = 192; classes1Span[6] = 192;
Fc.Value.Classes[1][7] = 198; classes1Span[7] = 198;
Fc.Value.Classes[1][8] = 198; classes1Span[8] = 198;
Fc.Value.Classes[1][9] = 208; classes1Span[9] = 208;
Fc.Value.Class0[1][0] = 208; class0Span[1][0] = 208;
Fc.Value.Bits[1][0] = 136; bits1Span[0] = 136;
Fc.Value.Bits[1][1] = 140; bits1Span[1] = 140;
Fc.Value.Bits[1][2] = 148; bits1Span[2] = 148;
Fc.Value.Bits[1][3] = 160; bits1Span[3] = 160;
Fc.Value.Bits[1][4] = 176; bits1Span[4] = 176;
Fc.Value.Bits[1][5] = 192; bits1Span[5] = 192;
Fc.Value.Bits[1][6] = 224; bits1Span[6] = 224;
Fc.Value.Bits[1][7] = 234; bits1Span[7] = 234;
Fc.Value.Bits[1][8] = 234; bits1Span[8] = 234;
Fc.Value.Bits[1][9] = 240; bits1Span[9] = 240;
Fc.Value.Class0Fp[1][0][0] = 128; class0Fp10Span[0] = 128;
Fc.Value.Class0Fp[1][0][1] = 128; class0Fp10Span[1] = 128;
Fc.Value.Class0Fp[1][0][2] = 64; class0Fp10Span[2] = 64;
Fc.Value.Class0Fp[1][1][0] = 96; class0Fp11Span[0] = 96;
Fc.Value.Class0Fp[1][1][1] = 112; class0Fp11Span[1] = 112;
Fc.Value.Class0Fp[1][1][2] = 64; class0Fp11Span[2] = 64;
Fc.Value.Fp[1][0] = 64; fp1Span[0] = 64;
Fc.Value.Fp[1][1] = 96; fp1Span[1] = 96;
Fc.Value.Fp[1][2] = 64; fp1Span[2] = 64;
Fc.Value.Class0Hp[1] = 160; class0HpSpan[1] = 160;
Fc.Value.Hp[1] = 128; hpSpan[1] = 128;
} }
public void AdaptMvProbs(bool allowHp) public void AdaptMvProbs(bool allowHp)
@@ -576,41 +629,74 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
counts.Joints.AsSpan(), counts.Joints.AsSpan(),
fc.Joints.AsSpan()); fc.Joints.AsSpan());
Span<byte> fSignSpan = fc.Sign.AsSpan();
Span<byte> pSignSpan = preFc.Sign.AsSpan();
Span<Array2<uint>> cSignSpan = counts.Sign.AsSpan();
Span<Array10<byte>> fClassesSpan = fc.Classes.AsSpan();
Span<Array10<byte>> pClassesSpan = preFc.Classes.AsSpan();
Span<Array11<uint>> cClassesSpan = counts.Classes.AsSpan();
Span<Array1<byte>> fClass0Span = fc.Class0.AsSpan();
Span<Array1<byte>> pClass0Span = preFc.Class0.AsSpan();
Span<Array2<uint>> cClass0Span = counts.Class0.AsSpan();
Span<Array10<byte>> fBitsSpan1 = fc.Bits.AsSpan();
Span<Array10<byte>> pBitsSpan1 = preFc.Bits.AsSpan();
Span<Array10<Array2<uint>>> cBitsSpan1 = counts.Bits.AsSpan();
Span<Array2<Array3<byte>>> fClass0FpSpan1 = fc.Class0Fp.AsSpan();
Span<Array2<Array3<byte>>> pClass0FpSpan1 = preFc.Class0Fp.AsSpan();
Span<Array2<Array4<uint>>> cClass0FpSpan1 = counts.Class0Fp.AsSpan();
Span<Array3<byte>> fFpSpan = fc.Fp.AsSpan();
Span<Array3<byte>> pFpSpan = preFc.Fp.AsSpan();
Span<Array4<uint>> cFpSpan = counts.Fp.AsSpan();
Span<byte> fClass0HpSpan = fc.Class0Hp.AsSpan();
Span<byte> pClass0HpSpan = preFc.Class0Hp.AsSpan();
Span<Array2<uint>> cClass0HpSpan = counts.Class0Hp.AsSpan();
Span<byte> fHpSpan = fc.Hp.AsSpan();
Span<byte> pHpSpan = preFc.Hp.AsSpan();
Span<Array2<uint>> cHpSpan = counts.Hp.AsSpan();
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
fc.Sign[i] = Prob.ModeMvMergeProbs(preFc.Sign[i], ref counts.Sign[i]); fSignSpan[i] = Prob.ModeMvMergeProbs(pSignSpan[i], cSignSpan[i].AsSpan());
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMv.ClassTree, EntropyMv.ClassTree,
preFc.Classes[i].AsSpan(), pClassesSpan[i].AsSpan(),
counts.Classes[i].AsSpan(), cClassesSpan[i].AsSpan(),
fc.Classes[i].AsSpan()); fClassesSpan[i].AsSpan());
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMv.Class0Tree, EntropyMv.Class0Tree,
preFc.Class0[i].AsSpan(), pClass0Span[i].AsSpan(),
counts.Class0[i].AsSpan(), cClass0Span[i].AsSpan(),
fc.Class0[i].AsSpan()); fClass0Span[i].AsSpan());
Span<byte> fBitsSpan2 = fBitsSpan1[i].AsSpan();
Span<byte> pBitsSpan2 = pBitsSpan1[i].AsSpan();
Span<Array2<uint>> cBitsSpan2 = cBitsSpan1[i].AsSpan();
for (int j = 0; j < EntropyMv.OffsetBits; ++j) for (int j = 0; j < EntropyMv.OffsetBits; ++j)
{ {
fc.Bits[i][j] = Prob.ModeMvMergeProbs(preFc.Bits[i][j], ref counts.Bits[i][j]); fBitsSpan2[j] = Prob.ModeMvMergeProbs(pBitsSpan2[j], cBitsSpan2[j].AsSpan());
} }
Span<Array3<byte>> fClass0FpSpan2 = fClass0FpSpan1[i].AsSpan();
Span<Array3<byte>> pClass0FpSpan2 = pClass0FpSpan1[i].AsSpan();
Span<Array4<uint>> cClass0FpSpan2 = cClass0FpSpan1[i].AsSpan();
for (int j = 0; j < EntropyMv.Class0Size; ++j) for (int j = 0; j < EntropyMv.Class0Size; ++j)
{ {
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMv.FpTree, EntropyMv.FpTree,
preFc.Class0Fp[i][j].AsSpan(), pClass0FpSpan2[j].AsSpan(),
counts.Class0Fp[i][j].AsSpan(), cClass0FpSpan2[j].AsSpan(),
fc.Class0Fp[i][j].AsSpan()); fClass0FpSpan2[j].AsSpan());
} }
Prob.VpxTreeMergeProbs(EntropyMv.FpTree, preFc.Fp[i].AsSpan(), counts.Fp[i].AsSpan(), Prob.VpxTreeMergeProbs(EntropyMv.FpTree, pFpSpan[i].AsSpan(), cFpSpan[i].AsSpan(),
fc.Fp[i].AsSpan()); fFpSpan[i].AsSpan());
if (allowHp) if (allowHp)
{ {
fc.Class0Hp[i] = Prob.ModeMvMergeProbs(preFc.Class0Hp[i], ref counts.Class0Hp[i]); fClass0HpSpan[i] = Prob.ModeMvMergeProbs(pClass0HpSpan[i], cClass0HpSpan[i].AsSpan());
fc.Hp[i] = Prob.ModeMvMergeProbs(preFc.Hp[i], ref counts.Hp[i]); fHpSpan[i] = Prob.ModeMvMergeProbs(pHpSpan[i], cHpSpan[i].AsSpan());
} }
} }
} }
@@ -769,75 +855,115 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx];
ref Vp9BackwardUpdates counts = ref Counts.Value; ref Vp9BackwardUpdates counts = ref Counts.Value;
Span<byte> fIntraInterProbSpan = fc.IntraInterProb.AsSpan();
Span<byte> pIntraInterProbSpan = preFc.IntraInterProb.AsSpan();
Span<Array2<uint>> cIntraInterSpan = counts.IntraInter.AsSpan();
for (int i = 0; i < Constants.IntraInterContexts; i++) for (int i = 0; i < Constants.IntraInterContexts; i++)
{ {
fc.IntraInterProb[i] = Prob.ModeMvMergeProbs(preFc.IntraInterProb[i], ref counts.IntraInter[i]); fIntraInterProbSpan[i] = Prob.ModeMvMergeProbs(pIntraInterProbSpan[i], cIntraInterSpan[i].AsSpan());
} }
Span<byte> fCompInterProbSpan = fc.CompInterProb.AsSpan();
Span<byte> pCompInterProbSpan = preFc.CompInterProb.AsSpan();
Span<Array2<uint>> cCompInterSpan = counts.CompInter.AsSpan();
for (int i = 0; i < Constants.CompInterContexts; i++) for (int i = 0; i < Constants.CompInterContexts; i++)
{ {
fc.CompInterProb[i] = Prob.ModeMvMergeProbs(preFc.CompInterProb[i], ref counts.CompInter[i]); fCompInterProbSpan[i] = Prob.ModeMvMergeProbs(pCompInterProbSpan[i], cCompInterSpan[i].AsSpan());
} }
Span<byte> fCompRefProbSpan = fc.CompRefProb.AsSpan();
Span<byte> pCompRefProbSpan = preFc.CompRefProb.AsSpan();
Span<Array2<uint>> cCompRefSpan = counts.CompRef.AsSpan();
for (int i = 0; i < Constants.RefContexts; i++) for (int i = 0; i < Constants.RefContexts; i++)
{ {
fc.CompRefProb[i] = Prob.ModeMvMergeProbs(preFc.CompRefProb[i], ref counts.CompRef[i]); fCompRefProbSpan[i] = Prob.ModeMvMergeProbs(pCompRefProbSpan[i], cCompRefSpan[i].AsSpan());
} }
Span<Array2<byte>> fSingleRefProbSpan1 = fc.SingleRefProb.AsSpan();
Span<Array2<byte>> pSingleRefProbSpan1 = preFc.SingleRefProb.AsSpan();
Span<Array2<Array2<uint>>> cSingleRefSpan1 = counts.SingleRef.AsSpan();
for (int i = 0; i < Constants.RefContexts; i++) for (int i = 0; i < Constants.RefContexts; i++)
{ {
Span<byte> fSingleRefProbSpan2 = fSingleRefProbSpan1[i].AsSpan();
Span<byte> pSingleRefProbSpan2 = pSingleRefProbSpan1[i].AsSpan();
Span<Array2<uint>> cSingleRefSpan2 = cSingleRefSpan1[i].AsSpan();
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
{ {
fc.SingleRefProb[i][j] = fSingleRefProbSpan2[j] =
Prob.ModeMvMergeProbs(preFc.SingleRefProb[i][j], ref counts.SingleRef[i][j]); Prob.ModeMvMergeProbs(pSingleRefProbSpan2[j], cSingleRefSpan2[j].AsSpan());
} }
} }
Span<Array3<byte>> fInterModeProbSpan = fc.InterModeProb.AsSpan();
Span<Array3<byte>> pInterModeProbSpan = preFc.InterModeProb.AsSpan();
Span<Array4<uint>> cInterModeSpan = counts.InterMode.AsSpan();
for (int i = 0; i < Constants.InterModeContexts; i++) for (int i = 0; i < Constants.InterModeContexts; i++)
{ {
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMode.InterModeTree, EntropyMode.InterModeTree,
preFc.InterModeProb[i].AsSpan(), pInterModeProbSpan[i].AsSpan(),
counts.InterMode[i].AsSpan(), cInterModeSpan[i].AsSpan(),
fc.InterModeProb[i].AsSpan()); fInterModeProbSpan[i].AsSpan());
} }
Span<Array9<byte>> fYModeProbSpan = fc.YModeProb.AsSpan();
Span<Array9<byte>> pYModeProbSpan = preFc.YModeProb.AsSpan();
Span<Array10<uint>> cYModeSpan = counts.YMode.AsSpan();
for (int i = 0; i < EntropyMode.BlockSizeGroups; i++) for (int i = 0; i < EntropyMode.BlockSizeGroups; i++)
{ {
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMode.IntraModeTree, EntropyMode.IntraModeTree,
preFc.YModeProb[i].AsSpan(), pYModeProbSpan[i].AsSpan(),
counts.YMode[i].AsSpan(), cYModeSpan[i].AsSpan(),
fc.YModeProb[i].AsSpan()); fYModeProbSpan[i].AsSpan());
} }
Span<Array9<byte>> fUvModeProbSpan = fc.UvModeProb.AsSpan();
Span<Array9<byte>> pUvModeProbSpan = preFc.UvModeProb.AsSpan();
Span<Array10<uint>> cUvModeSpan = counts.UvMode.AsSpan();
for (int i = 0; i < Constants.IntraModes; ++i) for (int i = 0; i < Constants.IntraModes; ++i)
{ {
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMode.IntraModeTree, EntropyMode.IntraModeTree,
preFc.UvModeProb[i].AsSpan(), pUvModeProbSpan[i].AsSpan(),
counts.UvMode[i].AsSpan(), cUvModeSpan[i].AsSpan(),
fc.UvModeProb[i].AsSpan()); fUvModeProbSpan[i].AsSpan());
} }
Span<Array3<byte>> fPartitionProbSpan = fc.PartitionProb.AsSpan();
Span<Array3<byte>> pPartitionProbSpan = preFc.PartitionProb.AsSpan();
Span<Array4<uint>> cPartitionSpan = counts.Partition.AsSpan();
for (int i = 0; i < Constants.PartitionContexts; i++) for (int i = 0; i < Constants.PartitionContexts; i++)
{ {
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMode.PartitionTree, EntropyMode.PartitionTree,
preFc.PartitionProb[i].AsSpan(), pPartitionProbSpan[i].AsSpan(),
counts.Partition[i].AsSpan(), cPartitionSpan[i].AsSpan(),
fc.PartitionProb[i].AsSpan()); fPartitionProbSpan[i].AsSpan());
} }
if (InterpFilter == Constants.Switchable) if (InterpFilter == Constants.Switchable)
{ {
Span<Array2<byte>> fSwitchableInterpProbSpan = fc.SwitchableInterpProb.AsSpan();
Span<Array2<byte>> pSwitchableInterpProbSpan = preFc.SwitchableInterpProb.AsSpan();
Span<Array3<uint>> cSwitchableInterpSpan = counts.SwitchableInterp.AsSpan();
for (int i = 0; i < Constants.SwitchableFilterContexts; i++) for (int i = 0; i < Constants.SwitchableFilterContexts; i++)
{ {
Prob.VpxTreeMergeProbs( Prob.VpxTreeMergeProbs(
EntropyMode.SwitchableInterpTree, EntropyMode.SwitchableInterpTree,
preFc.SwitchableInterpProb[i].AsSpan(), pSwitchableInterpProbSpan[i].AsSpan(),
counts.SwitchableInterp[i].AsSpan(), cSwitchableInterpSpan[i].AsSpan(),
fc.SwitchableInterpProb[i].AsSpan()); fSwitchableInterpProbSpan[i].AsSpan());
} }
} }
@@ -846,34 +972,62 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
Array1<Array2<uint>> branchCt8X8P = new(); Array1<Array2<uint>> branchCt8X8P = new();
Array2<Array2<uint>> branchCt16X16P = new(); Array2<Array2<uint>> branchCt16X16P = new();
Array3<Array2<uint>> branchCt32X32P = new(); Array3<Array2<uint>> branchCt32X32P = new();
Span<Array2<uint>> branchCt8X8PSpan = branchCt8X8P.AsSpan();
Span<Array2<uint>> branchCt16X16PSpan = branchCt16X16P.AsSpan();
Span<Array2<uint>> branchCt32X32PSpan = branchCt32X32P.AsSpan();
Span<Array2<uint>> tx8x8Span = counts.Tx8x8.AsSpan();
Span<Array2<uint>> tx16x16Span = counts.Tx8x8.AsSpan();
Span<Array2<uint>> tx32x32Span = counts.Tx8x8.AsSpan();
//There is no need for a Span2, as there is only ever 1 iteration
Span<Array1<byte>> fTx8x8ProbSpan = fc.Tx8x8Prob.AsSpan();
Span<Array1<byte>> pTx8x8ProbSpan = preFc.Tx8x8Prob.AsSpan();
Span<Array2<byte>> fTx16x16ProbSpan1 = fc.Tx16x16Prob.AsSpan();
Span<Array2<byte>> pTx16x16ProbSpan1 = preFc.Tx16x16Prob.AsSpan();
Span<Array3<byte>> fTx32x32ProbSpan1 = fc.Tx32x32Prob.AsSpan();
Span<Array3<byte>> pTx32x32ProbSpan1 = preFc.Tx32x32Prob.AsSpan();
for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) for (int i = 0; i < EntropyMode.TxSizeContexts; ++i)
{ {
EntropyMode.TxCountsToBranchCounts8X8(counts.Tx8x8[i].AsSpan(), ref branchCt8X8P); EntropyMode.TxCountsToBranchCounts8X8(tx8x8Span[i].AsSpan(), branchCt8X8P.AsSpan());
for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j) for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j)
{ {
fc.Tx8x8Prob[i][j] = Prob.ModeMvMergeProbs(preFc.Tx8x8Prob[i][j], ref branchCt8X8P[j]); fTx8x8ProbSpan[i][j] = Prob.ModeMvMergeProbs(pTx8x8ProbSpan[i][j], branchCt8X8PSpan[j].AsSpan());
} }
Span<byte> fTx16x16ProbSpan2 = fTx16x16ProbSpan1[i].AsSpan();
Span<byte> pTx16x16ProbSpan2 = pTx16x16ProbSpan1[i].AsSpan();
EntropyMode.TxCountsToBranchCounts16X16(counts.Tx16x16[i].AsSpan(), ref branchCt16X16P); EntropyMode.TxCountsToBranchCounts16X16(tx16x16Span[i].AsSpan(), branchCt16X16P.AsSpan());
for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j) for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j)
{ {
fc.Tx16x16Prob[i][j] = fTx16x16ProbSpan2[j] =
Prob.ModeMvMergeProbs(preFc.Tx16x16Prob[i][j], ref branchCt16X16P[j]); Prob.ModeMvMergeProbs(pTx16x16ProbSpan2[j], branchCt16X16PSpan[j].AsSpan());
} }
Span<byte> fTx32x32ProbSpan2 = fTx32x32ProbSpan1[i].AsSpan();
Span<byte> pTx32x32ProbSpan2 = pTx32x32ProbSpan1[i].AsSpan();
EntropyMode.TxCountsToBranchCounts32X32(counts.Tx32x32[i].AsSpan(), ref branchCt32X32P); EntropyMode.TxCountsToBranchCounts32X32(tx32x32Span[i].AsSpan(), branchCt32X32P.AsSpan());
for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j) for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j)
{ {
fc.Tx32x32Prob[i][j] = fTx32x32ProbSpan2[j] =
Prob.ModeMvMergeProbs(preFc.Tx32x32Prob[i][j], ref branchCt32X32P[j]); Prob.ModeMvMergeProbs(pTx32x32ProbSpan2[j], branchCt32X32PSpan[j].AsSpan());
} }
} }
} }
Span<byte> fSkipProbSpan = fc.SkipProb.AsSpan();
Span<byte> pSkipProbSpan = preFc.SkipProb.AsSpan();
Span<Array2<uint>> cSkipSpan = counts.Skip.AsSpan();
for (int i = 0; i < Constants.SkipContexts; ++i) for (int i = 0; i < Constants.SkipContexts; ++i)
{ {
fc.SkipProb[i] = Prob.ModeMvMergeProbs(preFc.SkipProb[i], ref counts.Skip[i]); fSkipProbSpan[i] = Prob.ModeMvMergeProbs(pSkipProbSpan[i], cSkipSpan[i].AsSpan());
} }
} }
@@ -916,13 +1070,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{ {
ref MvRef mv = ref PrevFrameMvs[i]; ref MvRef mv = ref PrevFrameMvs[i];
mv.Mv[0].Row = mvs[i].Mvs[0].Row; Span<Mv> mvSpan = mv.Mv.AsSpan();
mv.Mv[0].Col = mvs[i].Mvs[0].Col; Span<Vp9Mv> mvsSpan = mvs[i].Mvs.AsSpan();
mv.Mv[1].Row = mvs[i].Mvs[1].Row;
mv.Mv[1].Col = mvs[i].Mvs[1].Col;
mv.RefFrame[0] = (sbyte)mvs[i].RefFrames[0]; mvSpan[0].Row = mvsSpan[0].Row;
mv.RefFrame[1] = (sbyte)mvs[i].RefFrames[1]; mvSpan[0].Col = mvsSpan[0].Col;
mvSpan[1].Row = mvsSpan[1].Row;
mvSpan[1].Col = mvsSpan[1].Col;
Span<sbyte> refFrameSpan = mv.RefFrame.AsSpan();
Span<int> refFramesSpan = mvs[i].RefFrames.AsSpan();
refFrameSpan[0] = (sbyte)refFramesSpan[0];
refFrameSpan[1] = (sbyte)refFramesSpan[1];
} }
} }
@@ -937,47 +1097,76 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
for (int i = 0; i < mvs.Length; i++) for (int i = 0; i < mvs.Length; i++)
{ {
ref MvRef mv = ref CurFrameMvs[i]; ref MvRef mv = ref CurFrameMvs[i];
Span<Mv> mvSpan = mv.Mv.AsSpan();
Span<Vp9Mv> mvsSpan = mvs[i].Mvs.AsSpan();
mvs[i].Mvs[0].Row = mv.Mv[0].Row; mvsSpan[0].Row = mvSpan[0].Row;
mvs[i].Mvs[0].Col = mv.Mv[0].Col; mvsSpan[0].Col = mvSpan[0].Col;
mvs[i].Mvs[1].Row = mv.Mv[1].Row; mvsSpan[1].Row = mvSpan[1].Row;
mvs[i].Mvs[1].Col = mv.Mv[1].Col; mvsSpan[1].Col = mvSpan[1].Col;
Span<sbyte> refFrameSpan = mv.RefFrame.AsSpan();
Span<int> refFramesSpan = mvs[i].RefFrames.AsSpan();
mvs[i].RefFrames[0] = mv.RefFrame[0]; refFramesSpan[0] = refFrameSpan[0];
mvs[i].RefFrames[1] = mv.RefFrame[1]; refFramesSpan[1] = refFrameSpan[1];
} }
} }
private void AdaptCoefProbs(byte txSize, uint countSat, uint updateFactor) private void AdaptCoefProbs(byte txSize, uint countSat, uint updateFactor)
{ {
ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx];
ref Array2<Array2<Array6<Array6<Array3<byte>>>>> probs = ref Fc.Value.CoefProbs[txSize]; Span<Array2<Array6<Array6<Array3<byte>>>>> probsSpan1 = Fc.Value.CoefProbs[txSize].AsSpan();
ref Array2<Array2<Array6<Array6<Array3<byte>>>>> preProbs = ref preFc.CoefProbs[txSize]; Span<Array2<Array6<Array6<Array3<byte>>>>> preProbsSpan1 = preFc.CoefProbs[txSize].AsSpan();
ref Array2<Array2<Array6<Array6<Array4<uint>>>>> counts = ref Counts.Value.Coef[txSize]; Span<Array2<Array6<Array6<Array4<uint>>>>> countsSpan1 = Counts.Value.Coef[txSize].AsSpan();
ref Array2<Array2<Array6<Array6<uint>>>> eobCounts = ref Counts.Value.EobBranch[txSize]; Span<Array2<Array6<Array6<uint>>>> eobCountsSpan1 = Counts.Value.EobBranch[txSize].AsSpan();
for (int i = 0; i < Constants.PlaneTypes; ++i) for (int i = 0; i < Constants.PlaneTypes; ++i)
{ {
Span<Array6<Array6<Array3<byte>>>> probsSpan2 = probsSpan1[i].AsSpan();
Span<Array6<Array6<Array3<byte>>>> preProbsSpan2 = preProbsSpan1[i].AsSpan();
Span<Array6<Array6<Array4<uint>>>> countsSpan2 = countsSpan1[i].AsSpan();
Span<Array6<Array6<uint>>> eobCountsSpan2 = eobCountsSpan1[i].AsSpan();
for (int j = 0; j < Entropy.RefTypes; ++j) for (int j = 0; j < Entropy.RefTypes; ++j)
{ {
Span<Array6<Array3<byte>>> probsSpan3 = probsSpan2[j].AsSpan();
Span<Array6<Array3<byte>>> preProbsSpan3 = preProbsSpan2[j].AsSpan();
Span<Array6<Array4<uint>>> countsSpan3 = countsSpan2[j].AsSpan();
Span<Array6<uint>> eobCountsSpan3 = eobCountsSpan2[j].AsSpan();
for (int k = 0; k < Entropy.CoefBands; ++k) for (int k = 0; k < Entropy.CoefBands; ++k)
{ {
Span<Array3<byte>> probsSpan4 = probsSpan3[k].AsSpan();
Span<Array3<byte>> preProbsSpan4 = preProbsSpan3[k].AsSpan();
Span<Array4<uint>> countsSpan4 = countsSpan3[k].AsSpan();
Span<uint> eobCountsSpan4 = eobCountsSpan3[k].AsSpan();
for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l) for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l)
{ {
int n0 = (int)counts[i][j][k][l][Entropy.ZeroToken]; Span<byte> probsSpan5 = probsSpan4[l].AsSpan();
int n1 = (int)counts[i][j][k][l][Entropy.OneToken]; Span<byte> preProbsSpan5 = preProbsSpan4[l].AsSpan();
int n2 = (int)counts[i][j][k][l][Entropy.TwoToken]; Span<uint> countsSpan5 = countsSpan4[l].AsSpan();
int neob = (int)counts[i][j][k][l][Entropy.EobModelToken];
int n0 = (int)countsSpan5[Entropy.ZeroToken];
int n1 = (int)countsSpan5[Entropy.OneToken];
int n2 = (int)countsSpan5[Entropy.TwoToken];
int neob = (int)countsSpan5[Entropy.EobModelToken];
Array3<Array2<uint>> branchCt = new(); Array3<Array2<uint>> branchCt = new();
branchCt[0][0] = (uint)neob; Span<Array2<uint>> branchCtSpan = branchCt.AsSpan();
branchCt[0][1] = (uint)(eobCounts[i][j][k][l] - neob); Span<uint> branchCt0Span = branchCtSpan[0].AsSpan();
branchCt[1][0] = (uint)n0; Span<uint> branchCt1Span = branchCtSpan[1].AsSpan();
branchCt[1][1] = (uint)(n1 + n2); Span<uint> branchCt2Span = branchCtSpan[2].AsSpan();
branchCt[2][0] = (uint)n1; branchCt0Span[0] = (uint)neob;
branchCt[2][1] = (uint)n2; branchCt0Span[1] = (uint)(eobCountsSpan4[l] - neob);
branchCt1Span[0] = (uint)n0;
branchCt1Span[1] = (uint)(n1 + n2);
branchCt2Span[0] = (uint)n1;
branchCt2Span[1] = (uint)n2;
for (int m = 0; m < Entropy.UnconstrainedNodes; ++m) for (int m = 0; m < Entropy.UnconstrainedNodes; ++m)
{ {
probs[i][j][k][l][m] = Prob.MergeProbs(preProbs[i][j][k][l][m], ref branchCt[m], probsSpan5[m] = Prob.MergeProbs(preProbsSpan5[m], branchCt[m].AsSpan(),
countSat, updateFactor); countSat, updateFactor);
} }
} }

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Common;
using Ryujinx.Graphics.Video; using Ryujinx.Graphics.Video;
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types namespace Ryujinx.Graphics.Nvdec.Vp9.Types
@@ -16,20 +17,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public int NeedResync; // Wait for key/intra-only frame. public int NeedResync; // Wait for key/intra-only frame.
public int HoldRefBuf; // Hold the reference buffer. public int HoldRefBuf; // Hold the reference buffer.
private static void DecreaseRefCount(int idx, ref Array12<RefCntBuffer> frameBufs, ref BufferPool pool) private static void DecreaseRefCount(int idx, Span<RefCntBuffer> frameBuffs, ref BufferPool pool)
{ {
if (idx >= 0 && frameBufs[idx].RefCount > 0) if (idx >= 0 && frameBuffs[idx].RefCount > 0)
{ {
--frameBufs[idx].RefCount; --frameBuffs[idx].RefCount;
// A worker may only get a free framebuffer index when calling GetFreeFb. // A worker may only get a free framebuffer index when calling GetFreeFb.
// But the private buffer is not set up until finish decoding header. // But the private buffer is not set up until finish decoding header.
// So any error happens during decoding header, the frame_bufs will not // So any error happens during decoding header, the frame_bufs will not
// have valid priv buffer. // have valid priv buffer.
if (frameBufs[idx].Released == 0 && frameBufs[idx].RefCount == 0 && if (frameBuffs[idx].Released == 0 && frameBuffs[idx].RefCount == 0 &&
!frameBufs[idx].RawFrameBuffer.Priv.IsNull) !frameBuffs[idx].RawFrameBuffer.Priv.IsNull)
{ {
FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[idx].RawFrameBuffer); FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBuffs[idx].RawFrameBuffer);
frameBufs[idx].Released = 1; frameBuffs[idx].Released = 1;
} }
} }
} }
@@ -43,22 +44,32 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
cm.CheckMemError(ref cm.FrameContexts, cm.CheckMemError(ref cm.FrameContexts,
allocator.Allocate<Vp9EntropyProbs>(Constants.FrameContexts)); allocator.Allocate<Vp9EntropyProbs>(Constants.FrameContexts));
Span<Array10<Array9<byte>>> cKfYModeProbSpan1 = cm.Fc.Value.KfYModeProb.AsSpan();
for (int i = 0; i < EntropyMode.KfYModeProb.Length; i++) for (int i = 0; i < EntropyMode.KfYModeProb.Length; i++)
{ {
Span<Array9<byte>> cKfYModeProbSpan2 = cKfYModeProbSpan1[i].AsSpan();
for (int j = 0; j < EntropyMode.KfYModeProb[i].Length; j++) for (int j = 0; j < EntropyMode.KfYModeProb[i].Length; j++)
{ {
Span<byte> cKfYModeProbSpan3 = cKfYModeProbSpan2[j].AsSpan();
for (int k = 0; k < EntropyMode.KfYModeProb[i][j].Length; k++) for (int k = 0; k < EntropyMode.KfYModeProb[i][j].Length; k++)
{ {
cm.Fc.Value.KfYModeProb[i][j][k] = EntropyMode.KfYModeProb[i][j][k]; cKfYModeProbSpan3[k] = EntropyMode.KfYModeProb[i][j][k];
} }
} }
} }
Span<Array9<byte>> cKfUvModeProbSpan1 = cm.Fc.Value.KfUvModeProb.AsSpan();
for (int i = 0; i < EntropyMode.KfUvModeProb.Length; i++) for (int i = 0; i < EntropyMode.KfUvModeProb.Length; i++)
{ {
Span<byte> cKfUvModeProbSpan2 = cKfUvModeProbSpan1[i].AsSpan();
for (int j = 0; j < EntropyMode.KfUvModeProb[i].Length; j++) for (int j = 0; j < EntropyMode.KfUvModeProb[i].Length; j++)
{ {
cm.Fc.Value.KfUvModeProb[i][j] = EntropyMode.KfUvModeProb[i][j]; cKfUvModeProbSpan2[j] = EntropyMode.KfUvModeProb[i][j];
} }
} }
@@ -88,12 +99,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
[57, 15, 9], // l split, a not split [57, 15, 9], // l split, a not split
[12, 3, 3] // a/l both split [12, 3, 3] // a/l both split
]; ];
Span<Array3<byte>> cKfPartitionProbSpan1 = cm.Fc.Value.KfPartitionProb.AsSpan();
for (int i = 0; i < kfPartitionProbs.Length; i++) for (int i = 0; i < kfPartitionProbs.Length; i++)
{ {
Span<byte> cKfPartitionProbSpan2 = cKfPartitionProbSpan1[i].AsSpan();
for (int j = 0; j < kfPartitionProbs[i].Length; j++) for (int j = 0; j < kfPartitionProbs[i].Length; j++)
{ {
cm.Fc.Value.KfPartitionProb[i][j] = kfPartitionProbs[i][j]; cKfPartitionProbSpan2[j] = kfPartitionProbs[i][j];
} }
} }
@@ -101,11 +116,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
NeedResync = 1; NeedResync = 1;
Span<int> refFrameMapSpan = cm.RefFrameMap.AsSpan();
Span<int> nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan();
// Initialize the references to not point to any frame buffers. // Initialize the references to not point to any frame buffers.
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
cm.RefFrameMap[i] = -1; refFrameMapSpan[i] = -1;
cm.NextRefFrameMap[i] = -1; nextRefFrameMapSpan[i] = -1;
} }
cm.CurrentVideoFrame = 0; cm.CurrentVideoFrame = 0;
@@ -124,30 +142,34 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
int refIndex = 0, mask; int refIndex = 0, mask;
ref Vp9Common cm = ref Common; ref Vp9Common cm = ref Common;
ref BufferPool pool = ref cm.BufferPool.Value; ref BufferPool pool = ref cm.BufferPool.Value;
ref Array12<RefCntBuffer> frameBufs = ref cm.BufferPool.Value.FrameBufs; Span<RefCntBuffer> frameBufs = cm.BufferPool.Value.FrameBufs.AsSpan();
Span<int> refFrameMapSpan = cm.RefFrameMap.AsSpan();
Span<int> nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan();
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
for (mask = RefreshFrameFlags; mask != 0; mask >>= 1) for (mask = RefreshFrameFlags; mask != 0; mask >>= 1)
{ {
int oldIdx = cm.RefFrameMap[refIndex]; int oldIdx = refFrameMapSpan[refIndex];
// Current thread releases the holding of reference frame. // Current thread releases the holding of reference frame.
DecreaseRefCount(oldIdx, ref frameBufs, ref pool); DecreaseRefCount(oldIdx, frameBufs, ref pool);
// Release the reference frame in reference map. // Release the reference frame in reference map.
if ((mask & 1) != 0) if ((mask & 1) != 0)
{ {
DecreaseRefCount(oldIdx, ref frameBufs, ref pool); DecreaseRefCount(oldIdx, frameBufs, ref pool);
} }
cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex]; refFrameMapSpan[refIndex] = nextRefFrameMapSpan[refIndex];
++refIndex; ++refIndex;
} }
// Current thread releases the holding of reference frame. // Current thread releases the holding of reference frame.
for (; refIndex < Constants.RefFrames && cm.ShowExistingFrame == 0; ++refIndex) for (; refIndex < Constants.RefFrames && cm.ShowExistingFrame == 0; ++refIndex)
{ {
int oldIdx = cm.RefFrameMap[refIndex]; int oldIdx = refFrameMapSpan[refIndex];
DecreaseRefCount(oldIdx, ref frameBufs, ref pool); DecreaseRefCount(oldIdx, frameBufs, ref pool);
cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex]; refFrameMapSpan[refIndex] = nextRefFrameMapSpan[refIndex];
} }
HoldRefBuf = 0; HoldRefBuf = 0;
@@ -158,7 +180,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// Invalidate these references until the next frame starts. // Invalidate these references until the next frame starts.
for (refIndex = 0; refIndex < 3; refIndex++) for (refIndex = 0; refIndex < 3; refIndex++)
{ {
cm.FrameRefs[refIndex].Idx = RefBuffer.InvalidIdx; frameRefsSpan[refIndex].Idx = RefBuffer.InvalidIdx;
} }
} }
@@ -166,7 +188,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{ {
ref Vp9Common cm = ref Common; ref Vp9Common cm = ref Common;
ref BufferPool pool = ref cm.BufferPool.Value; ref BufferPool pool = ref cm.BufferPool.Value;
ref Array12<RefCntBuffer> frameBufs = ref cm.BufferPool.Value.FrameBufs; Span<RefCntBuffer> frameBufs = cm.BufferPool.Value.FrameBufs.AsSpan();
ArrayPtr<byte> source = psource; ArrayPtr<byte> source = psource;
CodecErr retcode = 0; CodecErr retcode = 0;
cm.Error.ErrorCode = CodecErr.Ok; cm.Error.ErrorCode = CodecErr.Ok;
@@ -177,10 +199,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// We do not know if the missing frame(s) was supposed to update // We do not know if the missing frame(s) was supposed to update
// any of the reference buffers, but we act conservative and // any of the reference buffers, but we act conservative and
// mark only the last buffer as corrupted. // mark only the last buffer as corrupted.
Span<RefBuffer> frameRefsSpan = cm.FrameRefs.AsSpan();
if (cm.FrameRefs[0].Idx > 0) if (frameRefsSpan[0].Idx > 0)
{ {
cm.FrameRefs[0].Buf.Corrupted = 1; frameRefsSpan[0].Buf.Corrupted = 1;
} }
} }
@@ -279,8 +303,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
ArrayPtr<byte> dataStart = data; ArrayPtr<byte> dataStart = data;
CodecErr res; CodecErr res;
Array8<uint> frameSizes = new(); Array8<uint> frameSizes = new();
Span<uint> frameSizesSpan = frameSizes.AsSpan();
res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out int frameCount);
res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, frameSizesSpan, out int frameCount);
if (res != CodecErr.Ok) if (res != CodecErr.Ok)
{ {
return res; return res;
@@ -292,7 +317,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
for (int i = 0; i < frameCount; ++i) for (int i = 0; i < frameCount; ++i)
{ {
ArrayPtr<byte> dataStartCopy = dataStart; ArrayPtr<byte> dataStartCopy = dataStart;
uint frameSize = frameSizes[i]; uint frameSize = frameSizesSpan[i];
if (frameSize > (uint)dataStart.Length) if (frameSize > (uint)dataStart.Length)
{ {
return CodecErr.CorruptFrame; return CodecErr.CorruptFrame;
@@ -343,7 +368,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
return data[0]; return data[0];
} }
public static CodecErr ParseSuperframeIndex(ArrayPtr<byte> data, ulong dataSz, ref Array8<uint> sizes, out int count) public static CodecErr ParseSuperframeIndex(ArrayPtr<byte> data, ulong dataSz, Span<uint> sizes, out int count)
{ {
// A chunk ending with a byte matching 0xc0 is an invalid chunk unless // A chunk ending with a byte matching 0xc0 is an invalid chunk unless
// it is a super frame index. If the last byte of real video compression // it is a super frame index. If the last byte of real video compression

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Video; using Ryujinx.Graphics.Video;
using System;
namespace Ryujinx.Graphics.Nvdec.Types.Vp9 namespace Ryujinx.Graphics.Nvdec.Types.Vp9
{ {
@@ -33,15 +34,24 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9
public BackwardUpdates(ref Vp9BackwardUpdates counts) public BackwardUpdates(ref Vp9BackwardUpdates counts)
{ {
InterModeCounts = new Array7<Array3<Array2<uint>>>(); InterModeCounts = new Array7<Array3<Array2<uint>>>();
Span<Array3<Array2<uint>>> interModeCountsSpan1 = InterModeCounts.AsSpan();
Span<Array4<uint>> interModeSpan1 = counts.InterMode.AsSpan();
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
{ {
InterModeCounts[i][0][0] = counts.InterMode[i][2]; Span<Array2<uint>> interModeCountsSpan2 = interModeCountsSpan1[i].AsSpan();
InterModeCounts[i][0][1] = counts.InterMode[i][0] + counts.InterMode[i][1] + counts.InterMode[i][3]; Span<uint> interModeCountsSpan20 = interModeCountsSpan2[0].AsSpan();
InterModeCounts[i][1][0] = counts.InterMode[i][0]; Span<uint> interModeCountsSpan21 = interModeCountsSpan2[1].AsSpan();
InterModeCounts[i][1][1] = counts.InterMode[i][1] + counts.InterMode[i][3]; Span<uint> interModeCountsSpan22 = interModeCountsSpan2[2].AsSpan();
InterModeCounts[i][2][0] = counts.InterMode[i][1]; Span<uint> interModeSpan2 = interModeSpan1[i].AsSpan();
InterModeCounts[i][2][1] = counts.InterMode[i][3];
interModeCountsSpan20[0] = interModeSpan2[2];
interModeCountsSpan20[1] = interModeSpan2[0] + interModeSpan2[1] + interModeSpan2[3];
interModeCountsSpan21[0] = interModeSpan2[0];
interModeCountsSpan21[1] = interModeSpan2[1] + interModeSpan2[3];
interModeCountsSpan22[0] = interModeSpan2[1];
interModeCountsSpan22[1] = interModeSpan2[3];
} }
YModeCounts = counts.YMode; YModeCounts = counts.YMode;

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Video; using Ryujinx.Graphics.Video;
using System;
namespace Ryujinx.Graphics.Nvdec.Types.Vp9 namespace Ryujinx.Graphics.Nvdec.Types.Vp9
{ {
@@ -46,57 +47,98 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9
public void Convert(ref Vp9EntropyProbs fc) public void Convert(ref Vp9EntropyProbs fc)
{ {
Span<Array10<Array9<byte>>> kfYModeProbSpan1 = fc.KfYModeProb.AsSpan();
Span<Array10<Array8<byte>>> kfYModeProbE0ToE7Span1 = KfYModeProbE0ToE7.AsSpan();
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
Span<Array9<byte>> kfYModeProbSpan2 = kfYModeProbSpan1[i].AsSpan();
Span<Array8<byte>> kfYModeProbE0ToE7Span2 = kfYModeProbE0ToE7Span1[i].AsSpan();
for (int j = 0; j < 10; j++) for (int j = 0; j < 10; j++)
{ {
Span<byte> kfYModeProbSpan3 = kfYModeProbSpan2[j].AsSpan();
Span<byte> kfYModeProbE0ToE7Span3 = kfYModeProbE0ToE7Span2[j].AsSpan();
for (int k = 0; k < 9; k++) for (int k = 0; k < 9; k++)
{ {
fc.KfYModeProb[i][j][k] = k < 8 ? KfYModeProbE0ToE7[i][j][k] : KfYModeProbE8[i][j]; kfYModeProbSpan3[k] = k < 8 ? kfYModeProbE0ToE7Span3[k] : KfYModeProbE8[i][j];
} }
} }
} }
fc.SegTreeProb = SegTreeProbs; fc.SegTreeProb = SegTreeProbs;
fc.SegPredProb = SegPredProbs; fc.SegPredProb = SegPredProbs;
Span<Array3<byte>> interModeProbSpan1 = fc.InterModeProb.AsSpan();
Span<Array4<byte>> gInterModeProbSpan1 = InterModeProb.AsSpan();
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
{ {
Span<byte> interModeProbSpan2 = interModeProbSpan1[i].AsSpan();
Span<byte> gInterModeProbSpan2 = gInterModeProbSpan1[i].AsSpan();
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
{ {
fc.InterModeProb[i][j] = InterModeProb[i][j]; interModeProbSpan2[j] = gInterModeProbSpan2[j];
} }
} }
fc.IntraInterProb = IntraInterProb; fc.IntraInterProb = IntraInterProb;
Span<Array9<byte>> kfUvModeProbSpan1 = fc.KfUvModeProb.AsSpan();
Span<Array8<byte>> kfUvModeProbE0ToE7Span1 = KfUvModeProbE0ToE7.AsSpan();
Span<Array9<byte>> uvModeProbSpan1 = fc.UvModeProb.AsSpan();
Span<Array8<byte>> uvModeProbE0ToE7Span1 = UvModeProbE0ToE7.AsSpan();
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
Span<byte> kfUvModeProbSpan2 = kfUvModeProbSpan1[i].AsSpan();
Span<byte> kfUvModeProbE0ToE7Span2 = kfUvModeProbE0ToE7Span1[i].AsSpan();
Span<byte> uvModeProbSpan2 = uvModeProbSpan1[i].AsSpan();
Span<byte> uvModeProbE0ToE7Span2 = uvModeProbE0ToE7Span1[i].AsSpan();
for (int j = 0; j < 9; j++) for (int j = 0; j < 9; j++)
{ {
fc.KfUvModeProb[i][j] = j < 8 ? KfUvModeProbE0ToE7[i][j] : KfUvModeProbE8[i]; kfUvModeProbSpan2[j] = j < 8 ? kfUvModeProbE0ToE7Span2[j] : KfUvModeProbE8[i];
fc.UvModeProb[i][j] = j < 8 ? UvModeProbE0ToE7[i][j] : UvModeProbE8[i]; uvModeProbSpan2[j] = j < 8 ? uvModeProbE0ToE7Span2[j] : UvModeProbE8[i];
} }
} }
fc.Tx8x8Prob = Tx8x8Prob; fc.Tx8x8Prob = Tx8x8Prob;
fc.Tx16x16Prob = Tx16x16Prob; fc.Tx16x16Prob = Tx16x16Prob;
fc.Tx32x32Prob = Tx32x32Prob; fc.Tx32x32Prob = Tx32x32Prob;
Span<Array9<byte>> yModeProbSpan1 = fc.YModeProb.AsSpan();
Span<Array8<byte>> yModeProbE0ToE7Span1 = YModeProbE0ToE7.AsSpan();
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
Span<byte> yModeProbSpan2 = yModeProbSpan1[i].AsSpan();
Span<byte> yModeProbE0ToE7Span2 = yModeProbE0ToE7Span1[i].AsSpan();
for (int j = 0; j < 9; j++) for (int j = 0; j < 9; j++)
{ {
fc.YModeProb[i][j] = j < 8 ? YModeProbE0ToE7[i][j] : YModeProbE8[i]; yModeProbSpan2[j] = j < 8 ? yModeProbE0ToE7Span2[j] : YModeProbE8[i];
} }
} }
Span<Array3<byte>> kfPartitionProbSpan1 = fc.KfPartitionProb.AsSpan();
Span<Array4<byte>> gKfPartitionProbSpan1 = KfPartitionProb.AsSpan();
Span<Array3<byte>> partitionProbSpan1 = fc.PartitionProb.AsSpan();
Span<Array4<byte>> gPartitionProbSpan1 = PartitionProb.AsSpan();
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
Span<byte> kfPartitionProbSpan2 = kfPartitionProbSpan1[i].AsSpan();
Span<byte> gKfPartitionProbSpan2 = gKfPartitionProbSpan1[i].AsSpan();
Span<byte> partitionProbSpan2 = partitionProbSpan1[i].AsSpan();
Span<byte> gPartitionProbSpan2 = gPartitionProbSpan1[i].AsSpan();
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
{ {
fc.KfPartitionProb[i][j] = KfPartitionProb[i][j]; kfPartitionProbSpan2[j] = gKfPartitionProbSpan2[j];
fc.PartitionProb[i][j] = PartitionProb[i][j]; partitionProbSpan2[j] = gPartitionProbSpan2[j];
} }
} }
@@ -116,20 +158,38 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9
fc.Bits = Bits; fc.Bits = Bits;
fc.SingleRefProb = SingleRefProb; fc.SingleRefProb = SingleRefProb;
fc.CompRefProb = CompRefProb; fc.CompRefProb = CompRefProb;
Span<Array2<Array2<Array6<Array6<Array3<byte>>>>>> coefProbsSpan1 = fc.CoefProbs.AsSpan();
Span<Array2<Array2<Array6<Array6<Array4<byte>>>>>> gCoefProbsSpan1 = CoefProbs.AsSpan();
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
Span<Array2<Array6<Array6<Array3<byte>>>>> coefProbsSpan2 = coefProbsSpan1[i].AsSpan();
Span<Array2<Array6<Array6<Array4<byte>>>>> gCoefProbsSpan2 = gCoefProbsSpan1[i].AsSpan();
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
{ {
Span<Array6<Array6<Array3<byte>>>> coefProbsSpan3 = coefProbsSpan2[j].AsSpan();
Span<Array6<Array6<Array4<byte>>>> gCoefProbsSpan3 = gCoefProbsSpan2[j].AsSpan();
for (int k = 0; k < 2; k++) for (int k = 0; k < 2; k++)
{ {
Span<Array6<Array3<byte>>> coefProbsSpan4 = coefProbsSpan3[k].AsSpan();
Span<Array6<Array4<byte>>> gCoefProbsSpan4 = gCoefProbsSpan3[k].AsSpan();
for (int l = 0; l < 6; l++) for (int l = 0; l < 6; l++)
{ {
Span<Array3<byte>> coefProbsSpan5 = coefProbsSpan4[l].AsSpan();
Span<Array4<byte>> gCoefProbsSpan5 = gCoefProbsSpan4[l].AsSpan();
for (int m = 0; m < 6; m++) for (int m = 0; m < 6; m++)
{ {
Span<byte> coefProbsSpan6 = coefProbsSpan5[m].AsSpan();
Span<byte> gCoefProbsSpan6 = gCoefProbsSpan5[m].AsSpan();
for (int n = 0; n < 3; n++) for (int n = 0; n < 3; n++)
{ {
fc.CoefProbs[i][j][k][l][m][n] = CoefProbs[i][j][k][l][m][n]; coefProbsSpan6[n] = gCoefProbsSpan6[n];
} }
} }
} }

View File

@@ -3,6 +3,7 @@ using System;
namespace Ryujinx.Graphics.Shader namespace Ryujinx.Graphics.Shader
{ {
[Flags]
public enum AttributeType : byte public enum AttributeType : byte
{ {
// Generic types. // Generic types.

View File

@@ -17,12 +17,12 @@ namespace Ryujinx.Graphics.Vic.Image
ResourceManager rm, ResourceManager rm,
ref SlotConfig config, ref SlotConfig config,
ref SlotSurfaceConfig surfaceConfig, ref SlotSurfaceConfig surfaceConfig,
ref Array8<PlaneOffsets> offsets) Span<PlaneOffsets> offsets)
{ {
switch (surfaceConfig.SlotPixelFormat) switch (surfaceConfig.SlotPixelFormat)
{ {
case PixelFormat.Y8___V8U8_N420: case PixelFormat.Y8___V8U8_N420:
return ReadNv12(rm, ref config, ref surfaceConfig, ref offsets); return ReadNv12(rm, ref config, ref surfaceConfig, offsets);
} }
Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{surfaceConfig.SlotPixelFormat}\"."); Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{surfaceConfig.SlotPixelFormat}\".");
@@ -37,9 +37,9 @@ namespace Ryujinx.Graphics.Vic.Image
ResourceManager rm, ResourceManager rm,
ref SlotConfig config, ref SlotConfig config,
ref SlotSurfaceConfig surfaceConfig, ref SlotSurfaceConfig surfaceConfig,
ref Array8<PlaneOffsets> offsets) Span<PlaneOffsets> offsets)
{ {
InputSurface input = ReadSurface(rm, ref config, ref surfaceConfig, ref offsets, 1, 2); InputSurface input = ReadSurface(rm, ref config, ref surfaceConfig, offsets, 1, 2);
int width = input.Width; int width = input.Width;
int height = input.Height; int height = input.Height;
@@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.Vic.Image
ResourceManager rm, ResourceManager rm,
ref SlotConfig config, ref SlotConfig config,
ref SlotSurfaceConfig surfaceConfig, ref SlotSurfaceConfig surfaceConfig,
ref Array8<PlaneOffsets> offsets, Span<PlaneOffsets> offsets,
int bytesPerPixel, int bytesPerPixel,
int planes) int planes)
{ {
@@ -301,17 +301,17 @@ namespace Ryujinx.Graphics.Vic.Image
if (planes > 0) if (planes > 0)
{ {
surface.SetBuffer0(ReadBuffer(rm, ref config, ref offsets, linear, 0, lw, lh, bytesPerPixel, gobBlocksInY)); surface.SetBuffer0(ReadBuffer(rm, ref config, offsets, linear, 0, lw, lh, bytesPerPixel, gobBlocksInY));
} }
if (planes > 1) if (planes > 1)
{ {
surface.SetBuffer1(ReadBuffer(rm, ref config, ref offsets, linear, 1, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY)); surface.SetBuffer1(ReadBuffer(rm, ref config, offsets, linear, 1, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY));
} }
if (planes > 2) if (planes > 2)
{ {
surface.SetBuffer2(ReadBuffer(rm, ref config, ref offsets, linear, 2, cw, ch, 1, gobBlocksInY)); surface.SetBuffer2(ReadBuffer(rm, ref config, offsets, linear, 2, cw, ch, 1, gobBlocksInY));
} }
return surface; return surface;
@@ -320,7 +320,7 @@ namespace Ryujinx.Graphics.Vic.Image
private static RentedBuffer ReadBuffer( private static RentedBuffer ReadBuffer(
ResourceManager rm, ResourceManager rm,
scoped ref SlotConfig config, scoped ref SlotConfig config,
scoped ref Array8<PlaneOffsets> offsets, Span<PlaneOffsets> offsets,
bool linear, bool linear,
int plane, int plane,
int width, int width,

View File

@@ -35,18 +35,21 @@ namespace Ryujinx.Graphics.Vic
config.OutputSurfaceConfig.OutSurfaceWidth + 1, config.OutputSurfaceConfig.OutSurfaceWidth + 1,
config.OutputSurfaceConfig.OutSurfaceHeight + 1); config.OutputSurfaceConfig.OutSurfaceHeight + 1);
for (int i = 0; i < config.SlotStruct.Length; i++) Span<SlotStruct> slotStructSpan = config.SlotStruct.AsSpan();
Span<Array8<PlaneOffsets>> setSurfacexSlotxSpan = _state.State.SetSurfacexSlotx.AsSpan();
for (int i = 0; i < slotStructSpan.Length; i++)
{ {
ref SlotStruct slot = ref config.SlotStruct[i]; ref SlotStruct slot = ref slotStructSpan[i];
if (!slot.SlotConfig.SlotEnable) if (!slot.SlotConfig.SlotEnable)
{ {
continue; continue;
} }
ref Array8<PlaneOffsets> offsets = ref _state.State.SetSurfacexSlotx[i]; Span<PlaneOffsets> offsets = setSurfacexSlotxSpan[i].AsSpan();
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets); using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, offsets);
int x1 = config.OutputConfig.TargetRectLeft; int x1 = config.OutputConfig.TargetRectLeft;
int y1 = config.OutputConfig.TargetRectTop; int y1 = config.OutputConfig.TargetRectTop;

View File

@@ -26,13 +26,17 @@ namespace Ryujinx.Graphics.Vulkan
public bool BecomesUnsetFrom(in BitMapStruct<T> from, ref BitMapStruct<T> into) public bool BecomesUnsetFrom(in BitMapStruct<T> from, ref BitMapStruct<T> into)
{ {
bool result = false; bool result = false;
Span<long> masksSpan = _masks.AsSpan();
Span<long> fMasksSpan = from._masks.AsSpan();
Span<long> iMasksSpan = into._masks.AsSpan();
int masks = _masks.Length; int masks = masksSpan.Length;
for (int i = 0; i < masks; i++) for (int i = 0; i < masks; i++)
{ {
long fromMask = from._masks[i]; long fromMask = fMasksSpan[i];
long unsetMask = (~fromMask) & (fromMask ^ _masks[i]); long unsetMask = (~fromMask) & (fromMask ^ masksSpan[i]);
into._masks[i] = unsetMask; iMasksSpan[i] = unsetMask;
result |= unsetMask != 0; result |= unsetMask != 0;
} }
@@ -49,11 +53,11 @@ namespace Ryujinx.Graphics.Vulkan
// Iterate the set bits in the result, and signal them. // Iterate the set bits in the result, and signal them.
int offset = 0; int offset = 0;
int masks = _masks.Length; Span<long> rMasksSpan = result._masks.AsSpan();
ref T resultMasks = ref result._masks; int masks = rMasksSpan.Length;
for (int i = 0; i < masks; i++) for (int i = 0; i < masks; i++)
{ {
long value = resultMasks[i]; long value = rMasksSpan[i];
while (value != 0) while (value != 0)
{ {
int bit = BitOperations.TrailingZeroCount((ulong)value); int bit = BitOperations.TrailingZeroCount((ulong)value);
@@ -75,10 +79,11 @@ namespace Ryujinx.Graphics.Vulkan
// Iterate the set bits in the result, and signal them. // Iterate the set bits in the result, and signal them.
int offset = 0; int offset = 0;
int masks = _masks.Length; Span<long> masksSpan = _masks.AsSpan();
int masks = masksSpan.Length;
for (int i = 0; i < masks; i++) for (int i = 0; i < masks; i++)
{ {
long value = _masks[i]; long value = masksSpan[i];
while (value != 0) while (value != 0)
{ {
int bit = BitOperations.TrailingZeroCount((ulong)value); int bit = BitOperations.TrailingZeroCount((ulong)value);
@@ -94,9 +99,11 @@ namespace Ryujinx.Graphics.Vulkan
public bool AnySet() public bool AnySet()
{ {
for (int i = 0; i < _masks.Length; i++) Span<long> masksSpan = _masks.AsSpan();
for (int i = 0; i < masksSpan.Length; i++)
{ {
if (_masks[i] != 0) if (masksSpan[i] != 0)
{ {
return true; return true;
} }
@@ -139,10 +146,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return true; return true;
} }
Span<long> masksSpan = _masks.AsSpan();
for (int i = startIndex + 1; i < endIndex; i++) for (int i = startIndex + 1; i < endIndex; i++)
{ {
if (_masks[i] != 0) if (masksSpan[i] != 0)
{ {
return true; return true;
} }
@@ -200,21 +209,23 @@ namespace Ryujinx.Graphics.Vulkan
int endIndex = end >> IntShift; int endIndex = end >> IntShift;
int endBit = end & IntMask; int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
Span<long> masksSpan = _masks.AsSpan();
if (startIndex == endIndex) if (startIndex == endIndex)
{ {
_masks[startIndex] |= startMask & endMask; masksSpan[startIndex] |= startMask & endMask;
} }
else else
{ {
_masks[startIndex] |= startMask; masksSpan[startIndex] |= startMask;
for (int i = startIndex + 1; i < endIndex; i++) for (int i = startIndex + 1; i < endIndex; i++)
{ {
_masks[i] |= -1L; masksSpan[i] |= -1L;
} }
_masks[endIndex] |= endMask; masksSpan[endIndex] |= endMask;
} }
} }
@@ -222,13 +233,13 @@ namespace Ryujinx.Graphics.Vulkan
{ {
BitMapStruct<T> result = new(); BitMapStruct<T> result = new();
ref T masks = ref _masks; Span<long> masksSpan = _masks.AsSpan();
ref T otherMasks = ref other._masks; Span<long> oMasksSpan = other._masks.AsSpan();
ref T newMasks = ref result._masks; Span<long> nMasksSpan = result._masks.AsSpan();
for (int i = 0; i < masks.Length; i++) for (int i = 0; i < masksSpan.Length; i++)
{ {
newMasks[i] = masks[i] | otherMasks[i]; nMasksSpan[i] = masksSpan[i] | oMasksSpan[i];
} }
return result; return result;
@@ -246,17 +257,21 @@ namespace Ryujinx.Graphics.Vulkan
public void Clear() public void Clear()
{ {
for (int i = 0; i < _masks.Length; i++) Span<long> masksSpan = _masks.AsSpan();
for (int i = 0; i < masksSpan.Length; i++)
{ {
_masks[i] = 0; masksSpan[i] = 0;
} }
} }
public void ClearInt(int start, int end) public void ClearInt(int start, int end)
{ {
Span<long> masksSpan = _masks.AsSpan();
for (int i = start; i <= end; i++) for (int i = start; i <= end; i++)
{ {
_masks[i] = 0; masksSpan[i] = 0;
} }
} }
} }

View File

@@ -680,7 +680,7 @@ namespace Ryujinx.Graphics.Vulkan
ShaderCollection program = _program; ShaderCollection program = _program;
if (_dirty.HasFlag(DirtyFlags.Uniform)) if ((_dirty & DirtyFlags.Uniform) == DirtyFlags.Uniform)
{ {
if (program.UsePushDescriptors) if (program.UsePushDescriptors)
{ {
@@ -692,12 +692,12 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
if (_dirty.HasFlag(DirtyFlags.Storage)) if ((_dirty & DirtyFlags.Storage) == DirtyFlags.Storage)
{ {
UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp);
} }
if (_dirty.HasFlag(DirtyFlags.Texture)) if ((_dirty & DirtyFlags.Texture) == DirtyFlags.Texture)
{ {
if (program.UpdateTexturesWithoutTemplate) if (program.UpdateTexturesWithoutTemplate)
{ {
@@ -709,7 +709,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
if (_dirty.HasFlag(DirtyFlags.Image)) if ((_dirty & DirtyFlags.Image) == DirtyFlags.Image)
{ {
UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp);
} }

View File

@@ -710,9 +710,11 @@ namespace Ryujinx.Graphics.Vulkan
public void SetBlendState(AdvancedBlendDescriptor blend) public void SetBlendState(AdvancedBlendDescriptor blend)
{ {
Span<PipelineColorBlendAttachmentState> colorBlendAttachmentStateSpan = _newState.Internal.ColorBlendAttachmentState.AsSpan();
for (int index = 0; index < Constants.MaxRenderTargets; index++) for (int index = 0; index < Constants.MaxRenderTargets; index++)
{ {
ref PipelineColorBlendAttachmentState vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; ref PipelineColorBlendAttachmentState vkBlend = ref colorBlendAttachmentStateSpan[index];
if (index == 0) if (index == 0)
{ {
@@ -985,10 +987,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length); int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length);
int writtenAttachments = 0; int writtenAttachments = 0;
Span<PipelineColorBlendAttachmentState> colorBlendAttachmentStateSpan = _newState.Internal.ColorBlendAttachmentState.AsSpan();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ref PipelineColorBlendAttachmentState vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; ref PipelineColorBlendAttachmentState vkBlend = ref colorBlendAttachmentStateSpan[i];
ColorComponentFlags newMask = (ColorComponentFlags)componentMask[i]; ColorComponentFlags newMask = (ColorComponentFlags)componentMask[i];
// When color write mask is 0, remove all blend state to help the pipeline cache. // When color write mask is 0, remove all blend state to help the pipeline cache.
@@ -1166,6 +1170,8 @@ namespace Ryujinx.Graphics.Vulkan
int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length); int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length);
uint dirtyVbSizes = 0; uint dirtyVbSizes = 0;
Span<VertexInputAttributeDescription> vertexAttributeDescriptionsSpan = _newState.Internal.VertexAttributeDescriptions.AsSpan();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -1179,7 +1185,7 @@ namespace Ryujinx.Graphics.Vulkan
dirtyVbSizes |= 1u << rawIndex; dirtyVbSizes |= 1u << rawIndex;
} }
_newState.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( vertexAttributeDescriptionsSpan[i] = new VertexInputAttributeDescription(
(uint)i, (uint)i,
(uint)bufferIndex, (uint)bufferIndex,
formatCapabilities.ConvertToVertexVkFormat(attribute.Format), formatCapabilities.ConvertToVertexVkFormat(attribute.Format),
@@ -1214,7 +1220,9 @@ namespace Ryujinx.Graphics.Vulkan
int validCount = 1; int validCount = 1;
BufferHandle lastHandle = default; BufferHandle lastHandle = default;
Auto<DisposableBuffer> lastBuffer = default; Auto<DisposableBuffer> lastBuffer = null;
Span<VertexInputBindingDescription> vertexBindingDescriptionsSpan = _newState.Internal.VertexBindingDescriptions.AsSpan();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -1236,7 +1244,7 @@ namespace Ryujinx.Graphics.Vulkan
int binding = i + 1; int binding = i + 1;
int descriptorIndex = validCount++; int descriptorIndex = validCount++;
_newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription( vertexBindingDescriptionsSpan[descriptorIndex] = new VertexInputBindingDescription(
(uint)binding, (uint)binding,
(uint)vertexBuffer.Stride, (uint)vertexBuffer.Stride,
inputRate); inputRate);
@@ -1405,6 +1413,9 @@ namespace Ryujinx.Graphics.Vulkan
// Look for textures that are masked out. // Look for textures that are masked out.
Span<PipelineColorBlendAttachmentState> colorBlendAttachmentStateSpan =
_newState.Internal.ColorBlendAttachmentState.AsSpan();
for (int i = 0; i < colors.Length; i++) for (int i = 0; i < colors.Length; i++)
{ {
if (colors[i] == null) if (colors[i] == null)
@@ -1412,7 +1423,7 @@ namespace Ryujinx.Graphics.Vulkan
continue; continue;
} }
ref PipelineColorBlendAttachmentState vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; ref PipelineColorBlendAttachmentState vkBlend = ref colorBlendAttachmentStateSpan[i];
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
{ {
@@ -1421,7 +1432,7 @@ namespace Ryujinx.Graphics.Vulkan
if (colors[i] == colors[j]) if (colors[i] == colors[j])
{ {
// Prefer the binding with no write mask. // Prefer the binding with no write mask.
ref PipelineColorBlendAttachmentState vkBlend2 = ref _newState.Internal.ColorBlendAttachmentState[j]; ref PipelineColorBlendAttachmentState vkBlend2 = ref colorBlendAttachmentStateSpan[j];
if (vkBlend.ColorWriteMask == 0) if (vkBlend.ColorWriteMask == 0)
{ {
colors[i] = null; colors[i] = null;

View File

@@ -2,7 +2,8 @@ using Ryujinx.Common;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using Format = Silk.NET.Vulkan.Format; using VulkanFormat = Silk.NET.Vulkan.Format;
using GALFormat = Ryujinx.Graphics.GAL.Format;
using PolygonMode = Silk.NET.Vulkan.PolygonMode; using PolygonMode = Silk.NET.Vulkan.PolygonMode;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
@@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments]; AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments];
Span<int> attachmentIndices = stackalloc int[MaxAttachments]; Span<int> attachmentIndices = stackalloc int[MaxAttachments];
Span<Format> attachmentFormats = stackalloc Format[MaxAttachments]; Span<VulkanFormat> attachmentFormats = stackalloc VulkanFormat[MaxAttachments];
int attachmentCount = 0; int attachmentCount = 0;
int colorCount = 0; int colorCount = 0;
@@ -32,14 +33,17 @@ namespace Ryujinx.Graphics.Vulkan
bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.DepthStencilFormat.IsImageCompatible(); !state.DepthStencilFormat.IsImageCompatible();
for (int i = 0; i < state.AttachmentEnable.Length; i++) Span<bool> attachmentEnableSpan = state.AttachmentEnable.AsSpan();
Span<GALFormat> attachmentFormatsSpan = state.AttachmentFormats.AsSpan();
for (int i = 0; i < attachmentEnableSpan.Length; i++)
{ {
if (state.AttachmentEnable[i]) if (attachmentEnableSpan[i])
{ {
bool isNotMsOrSupportsStorageAttachments = gd.Capabilities.SupportsShaderStorageImageMultisample || bool isNotMsOrSupportsStorageAttachments = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.AttachmentFormats[i].IsImageCompatible(); !attachmentFormatsSpan[i].IsImageCompatible();
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorageAttachments); attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(attachmentFormatsSpan[i], isNotMsOrSupportsStorageAttachments);
attachmentIndices[attachmentCount++] = i; attachmentIndices[attachmentCount++] = i;
colorCount++; colorCount++;
@@ -222,13 +226,15 @@ namespace Ryujinx.Graphics.Vulkan
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
Span<int> vbScalarSizes = stackalloc int[vbCount]; Span<int> vbScalarSizes = stackalloc int[vbCount];
Span<VertexAttribDescriptor> vertexAttribsSpan = state.VertexAttribs.AsSpan();
Span<VertexInputAttributeDescription> vertexAttributeDescriptionsSpan = pipeline.Internal.VertexAttributeDescriptions.AsSpan();
for (int i = 0; i < vaCount; i++) for (int i = 0; i < vaCount; i++)
{ {
VertexAttribDescriptor attribute = state.VertexAttribs[i]; VertexAttribDescriptor attribute = vertexAttribsSpan[i];
int bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1; int bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1;
pipeline.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( vertexAttributeDescriptionsSpan[i] = new VertexInputAttributeDescription(
(uint)i, (uint)i,
(uint)bufferIndex, (uint)bufferIndex,
gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format), gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format),
@@ -243,9 +249,12 @@ namespace Ryujinx.Graphics.Vulkan
int descriptorIndex = 1; int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex); pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
Span<BufferPipelineDescriptor> vertexBuffersSpan = state.VertexBuffers.AsSpan();
Span<VertexInputBindingDescription> vertexBindingDescriptionsSpan = pipeline.Internal.VertexBindingDescriptions.AsSpan();
for (int i = 0; i < vbCount; i++) for (int i = 0; i < vbCount; i++)
{ {
BufferPipelineDescriptor vertexBuffer = state.VertexBuffers[i]; BufferPipelineDescriptor vertexBuffer = vertexBuffersSpan[i];
if (vertexBuffer.Enable) if (vertexBuffer.Enable)
{ {
@@ -259,7 +268,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
// TODO: Support divisor > 1 // TODO: Support divisor > 1
pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription( vertexBindingDescriptionsSpan[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1, (uint)i + 1,
(uint)alignedStride, (uint)alignedStride,
inputRate); inputRate);
@@ -268,15 +277,19 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.VertexBindingDescriptionsCount = (uint)descriptorIndex; pipeline.VertexBindingDescriptionsCount = (uint)descriptorIndex;
Span<BlendDescriptor> blendDescriptorsSpan = state.BlendDescriptors.AsSpan();
Span<uint> colorWriteMaskSpan = state.ColorWriteMask.AsSpan();
Span<PipelineColorBlendAttachmentState> colorBlendAttachmentStateSpan = pipeline.Internal.ColorBlendAttachmentState.AsSpan();
// NOTE: Viewports, Scissors are dynamic. // NOTE: Viewports, Scissors are dynamic.
for (int i = 0; i < Constants.MaxRenderTargets; i++) for (int i = 0; i < Constants.MaxRenderTargets; i++)
{ {
BlendDescriptor blend = state.BlendDescriptors[i]; BlendDescriptor blend = blendDescriptorsSpan[i];
if (blend.Enable && state.ColorWriteMask[i] != 0) if (blend.Enable && colorWriteMaskSpan[i] != 0)
{ {
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( colorBlendAttachmentStateSpan[i] = new PipelineColorBlendAttachmentState(
blend.Enable, blend.Enable,
blend.ColorSrcFactor.Convert(), blend.ColorSrcFactor.Convert(),
blend.ColorDstFactor.Convert(), blend.ColorDstFactor.Convert(),
@@ -284,12 +297,12 @@ namespace Ryujinx.Graphics.Vulkan
blend.AlphaSrcFactor.Convert(), blend.AlphaSrcFactor.Convert(),
blend.AlphaDstFactor.Convert(), blend.AlphaDstFactor.Convert(),
blend.AlphaOp.Convert(), blend.AlphaOp.Convert(),
(ColorComponentFlags)state.ColorWriteMask[i]); (ColorComponentFlags)colorWriteMaskSpan[i]);
} }
else else
{ {
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( colorBlendAttachmentStateSpan[i] = new PipelineColorBlendAttachmentState(
colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]); colorWriteMask: (ColorComponentFlags)colorWriteMaskSpan[i]);
} }
} }
@@ -297,23 +310,27 @@ namespace Ryujinx.Graphics.Vulkan
int maxColorAttachmentIndex = -1; int maxColorAttachmentIndex = -1;
uint attachmentIntegerFormatMask = 0; uint attachmentIntegerFormatMask = 0;
bool allFormatsFloatOrSrgb = true; bool allFormatsFloatOrSrgb = true;
Span<bool> attachmentEnableSpan = state.AttachmentEnable.AsSpan();
Span<GALFormat> attachmentFormatsSpan = state.AttachmentFormats.AsSpan();
Span<VulkanFormat> pAttachmentFormatsSpan = pipeline.Internal.AttachmentFormats.AsSpan();
for (int i = 0; i < Constants.MaxRenderTargets; i++) for (int i = 0; i < Constants.MaxRenderTargets; i++)
{ {
if (state.AttachmentEnable[i]) if (attachmentEnableSpan[i])
{ {
bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.AttachmentFormats[i].IsImageCompatible(); !attachmentFormatsSpan[i].IsImageCompatible();
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorage); pAttachmentFormatsSpan[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(attachmentFormatsSpan[i], isNotMsOrSupportsStorage);
maxColorAttachmentIndex = i; maxColorAttachmentIndex = i;
if (state.AttachmentFormats[i].IsInteger()) if (attachmentFormatsSpan[i].IsInteger())
{ {
attachmentIntegerFormatMask |= 1u << i; attachmentIntegerFormatMask |= 1u << i;
} }
allFormatsFloatOrSrgb &= state.AttachmentFormats[i].IsFloatOrSrgb(); allFormatsFloatOrSrgb &= attachmentFormatsSpan[i].IsFloatOrSrgb();
} }
} }
@@ -322,7 +339,7 @@ namespace Ryujinx.Graphics.Vulkan
bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible() || bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible() ||
gd.Capabilities.SupportsShaderStorageImageMultisample; gd.Capabilities.SupportsShaderStorageImageMultisample;
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage); pAttachmentFormatsSpan[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage);
} }
pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);

View File

@@ -121,32 +121,32 @@ namespace Ryujinx.Graphics.Vulkan
{ {
Vk api = gd.Api; Vk api = gd.Api;
if (_dirty.HasFlag(DirtyFlags.Blend)) if ((_dirty & DirtyFlags.Blend) == DirtyFlags.Blend)
{ {
RecordBlend(api, commandBuffer); RecordBlend(api, commandBuffer);
} }
if (_dirty.HasFlag(DirtyFlags.DepthBias)) if ((_dirty & DirtyFlags.DepthBias) == DirtyFlags.DepthBias)
{ {
RecordDepthBias(api, commandBuffer); RecordDepthBias(api, commandBuffer);
} }
if (_dirty.HasFlag(DirtyFlags.Scissor)) if ((_dirty & DirtyFlags.Scissor) == DirtyFlags.Scissor)
{ {
RecordScissor(api, commandBuffer); RecordScissor(api, commandBuffer);
} }
if (_dirty.HasFlag(DirtyFlags.Stencil)) if ((_dirty & DirtyFlags.Stencil) == DirtyFlags.Stencil)
{ {
RecordStencilMasks(api, commandBuffer); RecordStencilMasks(api, commandBuffer);
} }
if (_dirty.HasFlag(DirtyFlags.Viewport)) if ((_dirty & DirtyFlags.Viewport) == DirtyFlags.Viewport)
{ {
RecordViewport(api, commandBuffer); RecordViewport(api, commandBuffer);
} }
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) if ((_dirty & DirtyFlags.FeedbackLoop) == DirtyFlags.FeedbackLoop && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{ {
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer); RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
} }

View File

@@ -520,6 +520,9 @@ namespace Ryujinx.Graphics.Vulkan
}; };
uint blendEnables = 0; uint blendEnables = 0;
Span<PipelineColorBlendAttachmentState> colorBlendAttachmentStateSpan =
Internal.ColorBlendAttachmentState.AsSpan();
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0) if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{ {
@@ -530,12 +533,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask); int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
if (Internal.ColorBlendAttachmentState[i].BlendEnable) if (colorBlendAttachmentStateSpan[i].BlendEnable)
{ {
blendEnables |= 1u << i; blendEnables |= 1u << i;
} }
Internal.ColorBlendAttachmentState[i].BlendEnable = false; colorBlendAttachmentStateSpan[i].BlendEnable = false;
attachmentIntegerFormatMask &= ~(1u << i); attachmentIntegerFormatMask &= ~(1u << i);
} }
} }
@@ -656,7 +659,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int i = BitOperations.TrailingZeroCount(blendEnables); int i = BitOperations.TrailingZeroCount(blendEnables);
Internal.ColorBlendAttachmentState[i].BlendEnable = true; colorBlendAttachmentStateSpan[i].BlendEnable = true;
blendEnables &= ~(1u << i); blendEnables &= ~(1u << i);
} }
} }
@@ -675,14 +678,21 @@ namespace Ryujinx.Graphics.Vulkan
// To work around this, we reduce the format to something that doesn't exceed the stride if possible. // To work around this, we reduce the format to something that doesn't exceed the stride if possible.
// The assumption is that the exceeding components are not actually accessed on the shader. // The assumption is that the exceeding components are not actually accessed on the shader.
Span<VertexInputAttributeDescription> vertexAttributeDescriptionsSpan =
Internal.VertexAttributeDescriptions.AsSpan();
Span<VertexInputBindingDescription> vertexBindingDescriptionsSpan =
Internal.VertexBindingDescriptions.AsSpan();
Span<VertexInputAttributeDescription> vertexAttributeDescriptions2Span =
_vertexAttributeDescriptions2.AsSpan();
for (int index = 0; index < VertexAttributeDescriptionsCount; index++) for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
{ {
VertexInputAttributeDescription attribute = Internal.VertexAttributeDescriptions[index]; VertexInputAttributeDescription attribute = vertexAttributeDescriptionsSpan[index];
int vbIndex = GetVertexBufferIndex(attribute.Binding); int vbIndex = GetVertexBufferIndex(attribute.Binding);
if (vbIndex >= 0) if (vbIndex >= 0)
{ {
ref VertexInputBindingDescription vb = ref Internal.VertexBindingDescriptions[vbIndex]; ref VertexInputBindingDescription vb = ref vertexBindingDescriptionsSpan[vbIndex];
Format format = attribute.Format; Format format = attribute.Format;
@@ -707,15 +717,18 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
_vertexAttributeDescriptions2[index] = attribute; vertexAttributeDescriptions2Span[index] = attribute;
} }
} }
private int GetVertexBufferIndex(uint binding) private int GetVertexBufferIndex(uint binding)
{ {
Span<VertexInputBindingDescription> vertexBindingDescriptionsSpan =
Internal.VertexBindingDescriptions.AsSpan();
for (int index = 0; index < VertexBindingDescriptionsCount; index++) for (int index = 0; index < VertexBindingDescriptionsCount; index++)
{ {
if (Internal.VertexBindingDescriptions[index].Binding == binding) if (vertexBindingDescriptionsSpan[index].Binding == binding)
{ {
return index; return index;
} }

View File

@@ -86,37 +86,45 @@ namespace Ryujinx.Graphics.Vulkan
Id6 * 23 ^ Id6 * 23 ^
Id7 * 23 ^ Id7 * 23 ^
Id8 * 23; Id8 * 23;
ReadOnlySpan<VertexInputAttributeDescription> vertexAttributeDescriptionsSpan = VertexAttributeDescriptions.AsSpan();
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
{ {
hash64 ^= VertexAttributeDescriptions[i].Binding * 23; hash64 ^= vertexAttributeDescriptionsSpan[i].Binding * 23;
hash64 ^= (uint)VertexAttributeDescriptions[i].Format * 23; hash64 ^= (uint)vertexAttributeDescriptionsSpan[i].Format * 23;
hash64 ^= VertexAttributeDescriptions[i].Location * 23; hash64 ^= vertexAttributeDescriptionsSpan[i].Location * 23;
hash64 ^= VertexAttributeDescriptions[i].Offset * 23; hash64 ^= vertexAttributeDescriptionsSpan[i].Offset * 23;
} }
ReadOnlySpan<VertexInputBindingDescription> vertexBindingDescriptionsSpan = VertexBindingDescriptions.AsSpan();
for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++) for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++)
{ {
hash64 ^= VertexBindingDescriptions[i].Binding * 23; hash64 ^= vertexBindingDescriptionsSpan[i].Binding * 23;
hash64 ^= (uint)VertexBindingDescriptions[i].InputRate * 23; hash64 ^= (uint)vertexBindingDescriptionsSpan[i].InputRate * 23;
hash64 ^= VertexBindingDescriptions[i].Stride * 23; hash64 ^= vertexBindingDescriptionsSpan[i].Stride * 23;
} }
ReadOnlySpan<PipelineColorBlendAttachmentState> colorBlendAttachmentStateSpan = ColorBlendAttachmentState.AsSpan();
for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++)
{ {
hash64 ^= ColorBlendAttachmentState[i].BlendEnable * 23; hash64 ^= colorBlendAttachmentStateSpan[i].BlendEnable * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].SrcColorBlendFactor * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].SrcColorBlendFactor * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].DstColorBlendFactor * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].DstColorBlendFactor * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].ColorBlendOp * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].ColorBlendOp * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].SrcAlphaBlendFactor * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].SrcAlphaBlendFactor * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].DstAlphaBlendFactor * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].DstAlphaBlendFactor * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].AlphaBlendOp * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].AlphaBlendOp * 23;
hash64 ^= (uint)ColorBlendAttachmentState[i].ColorWriteMask * 23; hash64 ^= (uint)colorBlendAttachmentStateSpan[i].ColorWriteMask * 23;
} }
ReadOnlySpan<Format> attachmentFormatsSpan = AttachmentFormats.AsSpan();
for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++)
{ {
hash64 ^= (uint)AttachmentFormats[i] * 23; hash64 ^= (uint)attachmentFormatsSpan[i] * 23;
} }
return (int)hash64 ^ ((int)(hash64 >> 32) * 17); return (int)hash64 ^ ((int)(hash64 >> 32) * 17);

View File

@@ -501,53 +501,13 @@ namespace Ryujinx.HLE.FileSystem
using FileStream file = File.OpenRead(keysSource); using FileStream file = File.OpenRead(keysSource);
switch (info.Extension) if (info.Extension is ".keys")
{ {
case ".zip": VerifyKeysFile(keysSource);
using (ZipArchive archive = ZipFile.OpenRead(keysSource)) File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
{ }
InstallKeysFromZip(archive, installDirectory); else
} throw new InvalidFirmwarePackageException("Input file is not a valid key package");
break;
case ".keys":
VerifyKeysFile(keysSource);
File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
break;
default:
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
}
}
private static void InstallKeysFromZip(ZipArchive archive, string installDirectory)
{
string temporaryDirectory = Path.Combine(installDirectory, "temp");
if (Directory.Exists(temporaryDirectory))
{
Directory.Delete(temporaryDirectory, true);
}
Directory.CreateDirectory(temporaryDirectory);
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (Path.GetExtension(entry.FullName).Equals(".keys", StringComparison.OrdinalIgnoreCase))
{
string extractDestination = Path.Combine(temporaryDirectory, entry.Name);
entry.ExtractToFile(extractDestination, overwrite: true);
try
{
VerifyKeysFile(extractDestination);
File.Move(extractDestination, Path.Combine(installDirectory, entry.Name), true);
}
catch (Exception)
{
Directory.Delete(temporaryDirectory, true);
throw;
}
}
}
Directory.Delete(temporaryDirectory, true);
} }
private void FinishInstallation(string temporaryDirectory, string registeredDirectory) private void FinishInstallation(string temporaryDirectory, string registeredDirectory)

View File

@@ -865,7 +865,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
lock (_threadingLock) lock (_threadingLock)
{ {
thread.ProcessListNode = _threads.AddLast(thread); _threads.AddLast(thread.ProcessListNode);
} }
} }
@@ -1227,7 +1227,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
thread.Suspend(ThreadSchedState.ThreadPauseFlag); thread.Suspend(ThreadSchedState.ThreadPauseFlag);
thread.Context.RequestInterrupt(); thread.Context.RequestInterrupt();
if (!thread.DebugHalt.WaitOne(TimeSpan.FromMilliseconds(50))) if (!thread.DebugHalt.Wait(TimeSpan.FromMilliseconds(50)))
{ {
Logger.Warning?.Print(LogClass.Kernel, $"Failed to suspend thread {thread.ThreadUid} in time."); Logger.Warning?.Print(LogClass.Kernel, $"Failed to suspend thread {thread.ThreadUid} in time.");
} }

View File

@@ -13,16 +13,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Monitor.Exit(mutex); Monitor.Exit(mutex);
currentThread.Withholder = threadList;
currentThread.Reschedule(ThreadSchedState.Paused);
currentThread.WithholderNode = threadList.AddLast(currentThread);
if (currentThread.TerminationRequested) if (currentThread.TerminationRequested)
{ {
threadList.Remove(currentThread.WithholderNode);
currentThread.Reschedule(ThreadSchedState.Running); currentThread.Reschedule(ThreadSchedState.Running);
currentThread.Withholder = null; currentThread.Withholder = null;
@@ -31,6 +23,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} }
else else
{ {
currentThread.Withholder = threadList;
currentThread.Reschedule(ThreadSchedState.Paused);
threadList.AddLast(currentThread.WithholderNode);
if (timeout > 0) if (timeout > 0)
{ {
context.TimeManager.ScheduleFutureInvocation(currentThread, timeout); context.TimeManager.ScheduleFutureInvocation(currentThread, timeout);

View File

@@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// even if they are not scheduled on guest cores. // even if they are not scheduled on guest cores.
if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running) if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running)
{ {
currentThread.SchedulerWaitEvent.WaitOne(); currentThread.SchedulerWaitEvent.Wait();
} }
} }
} }

View File

@@ -194,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return; return;
} }
thread.SiblingsPerCore[core] = SuggestedQueue(prio, core).AddFirst(thread); SuggestedQueue(prio, core).AddFirst(thread.SiblingsPerCore[core]);
_suggestedPrioritiesPerCore[core] |= 1L << prio; _suggestedPrioritiesPerCore[core] |= 1L << prio;
} }
@@ -223,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return; return;
} }
thread.SiblingsPerCore[core] = ScheduledQueue(prio, core).AddLast(thread); ScheduledQueue(prio, core).AddLast(thread.SiblingsPerCore[core]);
_scheduledPrioritiesPerCore[core] |= 1L << prio; _scheduledPrioritiesPerCore[core] |= 1L << prio;
} }
@@ -235,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return; return;
} }
thread.SiblingsPerCore[core] = ScheduledQueue(prio, core).AddFirst(thread); ScheduledQueue(prio, core).AddFirst(thread.SiblingsPerCore[core]);
_scheduledPrioritiesPerCore[core] |= 1L << prio; _scheduledPrioritiesPerCore[core] |= 1L << prio;
} }
@@ -251,7 +251,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
queue.Remove(thread.SiblingsPerCore[core]); queue.Remove(thread.SiblingsPerCore[core]);
thread.SiblingsPerCore[core] = queue.AddLast(thread); queue.AddLast(thread.SiblingsPerCore[core]);
return queue.First.Value; return queue.First.Value;
} }

View File

@@ -318,11 +318,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (nextThread == null) if (nextThread == null)
{ {
ActivateIdleThread(); ActivateIdleThread();
currentThread.SchedulerWaitEvent.WaitOne(); currentThread.SchedulerWaitEvent.Wait();
} }
else else
{ {
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent); nextThread.SchedulerWaitEvent.Set();
currentThread.SchedulerWaitEvent.Wait();
} }
} }
else else
@@ -578,7 +579,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
KThread selectedSuggestedCore = context.Schedulers[suggestedCore]._state.SelectedThread; KThread selectedSuggestedCore = context.Schedulers[suggestedCore]._state.SelectedThread;
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2)) if (selectedSuggestedCore == suggested || selectedSuggestedCore is { DynamicPriority: < 2 })
{ {
continue; continue;
} }

View File

@@ -1,3 +1,4 @@
using ARMeilleure.State;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.Debugger; using Ryujinx.HLE.Debugger;
@@ -38,9 +39,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public const int MaxWaitSyncObjects = 64; public const int MaxWaitSyncObjects = 64;
private ManualResetEvent _schedulerWaitEvent; private ManualResetEventSlim _schedulerWaitEvent;
public ManualResetEvent SchedulerWaitEvent => _schedulerWaitEvent; public ManualResetEventSlim SchedulerWaitEvent => _schedulerWaitEvent;
public Thread HostThread { get; private set; } public Thread HostThread { get; private set; }
@@ -92,6 +93,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private LinkedListNode<KThread> _mutexWaiterNode; private LinkedListNode<KThread> _mutexWaiterNode;
private readonly LinkedList<KThread> _pinnedWaiters; private readonly LinkedList<KThread> _pinnedWaiters;
private LinkedListNode<KThread> _pinnedWaiterNode;
public KThread MutexOwner { get; private set; } public KThread MutexOwner { get; private set; }
@@ -134,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private readonly Lock _activityOperationLock = new(); private readonly Lock _activityOperationLock = new();
internal readonly ManualResetEvent DebugHalt = new(false); internal readonly ManualResetEventSlim DebugHalt = new(false);
public KThread(KernelContext context) : base(context) public KThread(KernelContext context) : base(context)
{ {
@@ -143,8 +145,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount]; SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
for (int i = 0; i < SiblingsPerCore.Length; i++)
{
SiblingsPerCore[i] = new LinkedListNode<KThread>(this);
}
_mutexWaiters = []; _mutexWaiters = [];
_pinnedWaiters = []; _pinnedWaiters = [];
WithholderNode = new LinkedListNode<KThread>(this);
ProcessListNode = new LinkedListNode<KThread>(this);
_mutexWaiterNode = new LinkedListNode<KThread>(this);
_pinnedWaiterNode = new LinkedListNode<KThread>(this);
} }
public Result Initialize( public Result Initialize(
@@ -630,7 +642,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
break; break;
} }
_pinnedWaiters.AddLast(currentThread); _pinnedWaiters.AddLast(_pinnedWaiterNode);
currentThread.Reschedule(ThreadSchedState.Paused); currentThread.Reschedule(ThreadSchedState.Paused);
} }
@@ -684,17 +696,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
const int MaxFpuRegistersAArch32 = 16; const int MaxFpuRegistersAArch32 = 16;
ThreadContext context = new(); ThreadContext context = new();
Span<ulong> registersSpan = context.Registers.AsSpan();
Span<V128> fpuRegistersSpan = context.FpuRegisters.AsSpan();
if (Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit)) if (Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit))
{ {
for (int i = 0; i < context.Registers.Length; i++) for (int i = 0; i < registersSpan.Length; i++)
{ {
context.Registers[i] = Context.GetX(i); registersSpan[i] = Context.GetX(i);
} }
for (int i = 0; i < context.FpuRegisters.Length; i++) for (int i = 0; i < fpuRegistersSpan.Length; i++)
{ {
context.FpuRegisters[i] = Context.GetV(i); fpuRegistersSpan[i] = Context.GetV(i);
} }
context.Fp = Context.GetX(29); context.Fp = Context.GetX(29);
@@ -708,12 +723,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
for (int i = 0; i < MaxRegistersAArch32; i++) for (int i = 0; i < MaxRegistersAArch32; i++)
{ {
context.Registers[i] = (uint)Context.GetX(i); registersSpan[i] = (uint)Context.GetX(i);
} }
for (int i = 0; i < MaxFpuRegistersAArch32; i++) for (int i = 0; i < MaxFpuRegistersAArch32; i++)
{ {
context.FpuRegisters[i] = Context.GetV(i); fpuRegistersSpan[i] = Context.GetV(i);
} }
context.Pc = (uint)Context.Pc; context.Pc = (uint)Context.Pc;
@@ -844,7 +859,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return KernelResult.ThreadTerminating; return KernelResult.ThreadTerminating;
} }
_pinnedWaiters.AddLast(currentThread); _pinnedWaiters.AddLast(_pinnedWaiterNode);
currentThread.Reschedule(ThreadSchedState.Paused); currentThread.Reschedule(ThreadSchedState.Paused);
} }
@@ -1258,7 +1273,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
if (_schedulerWaitEvent == null) if (_schedulerWaitEvent == null)
{ {
ManualResetEvent schedulerWaitEvent = new(false); ManualResetEventSlim schedulerWaitEvent = new(false);
if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null) if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null)
{ {
@@ -1273,7 +1288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private void ThreadStart() private void ThreadStart()
{ {
_schedulerWaitEvent.WaitOne(); _schedulerWaitEvent.Wait();
DebugHalt.Reset(); DebugHalt.Reset();
KernelStatic.SetKernelContext(KernelContext, this); KernelStatic.SetKernelContext(KernelContext, this);

View File

@@ -31,11 +31,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
private readonly KEvent _friendInvitationStorageChannelEvent; private readonly KEvent _friendInvitationStorageChannelEvent;
private readonly KEvent _notificationStorageChannelEvent; private readonly KEvent _notificationStorageChannelEvent;
private readonly KEvent _healthWarningDisappearedSystemEvent; private readonly KEvent _healthWarningDisappearedSystemEvent;
private readonly KEvent _unknownEvent;
private int _gpuErrorDetectedSystemEventHandle; private int _gpuErrorDetectedSystemEventHandle;
private int _friendInvitationStorageChannelEventHandle; private int _friendInvitationStorageChannelEventHandle;
private int _notificationStorageChannelEventHandle; private int _notificationStorageChannelEventHandle;
private int _healthWarningDisappearedSystemEventHandle; private int _healthWarningDisappearedSystemEventHandle;
private int _unknownEventHandle;
private bool _gamePlayRecordingState; private bool _gamePlayRecordingState;
@@ -50,6 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
_friendInvitationStorageChannelEvent = new KEvent(system.KernelContext); _friendInvitationStorageChannelEvent = new KEvent(system.KernelContext);
_notificationStorageChannelEvent = new KEvent(system.KernelContext); _notificationStorageChannelEvent = new KEvent(system.KernelContext);
_healthWarningDisappearedSystemEvent = new KEvent(system.KernelContext); _healthWarningDisappearedSystemEvent = new KEvent(system.KernelContext);
_unknownEvent = new KEvent(system.KernelContext);
_horizon = system.LibHacHorizonManager.AmClient; _horizon = system.LibHacHorizonManager.AmClient;
} }
@@ -647,6 +650,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(210)] // 20.0.0+
// GetUnknownEvent() -> handle<copy>
public ResultCode GetUnknownEvent(ServiceCtx context)
{
if (_unknownEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_unknownEvent.ReadableEvent, out _unknownEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_unknownEventHandle);
return ResultCode.Success;
}
[CommandCmif(1001)] // 10.0.0+ [CommandCmif(1001)] // 10.0.0+
// PrepareForJit() // PrepareForJit()

View File

@@ -75,17 +75,21 @@ namespace Ryujinx.HLE.HOS.Services.Fatal
{ {
errorReport.AppendLine("\tStackTrace:"); errorReport.AppendLine("\tStackTrace:");
Span<ulong> stackTraceSpan = cpuContext64.StackTrace.AsSpan();
for (int i = 0; i < cpuContext64.StackTraceSize; i++) for (int i = 0; i < cpuContext64.StackTraceSize; i++)
{ {
errorReport.AppendLine($"\t\t0x{cpuContext64.StackTrace[i]:x16}"); errorReport.AppendLine($"\t\t0x{stackTraceSpan[i]:x16}");
} }
} }
errorReport.AppendLine("\tRegisters:"); errorReport.AppendLine("\tRegisters:");
Span<ulong> xSpan = cpuContext64.X.AsSpan();
for (int i = 0; i < cpuContext64.X.Length; i++) for (int i = 0; i < xSpan.Length; i++)
{ {
errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext64.X[i]:x16}"); errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{xSpan[i]:x16}");
} }
errorReport.AppendLine(); errorReport.AppendLine();
@@ -109,18 +113,22 @@ namespace Ryujinx.HLE.HOS.Services.Fatal
if (cpuContext32.StackTraceSize > 0) if (cpuContext32.StackTraceSize > 0)
{ {
errorReport.AppendLine("\tStackTrace:"); errorReport.AppendLine("\tStackTrace:");
Span<uint> stackTraceSpan = cpuContext32.StackTrace.AsSpan();
for (int i = 0; i < cpuContext32.StackTraceSize; i++) for (int i = 0; i < cpuContext32.StackTraceSize; i++)
{ {
errorReport.AppendLine($"\t\t0x{cpuContext32.StackTrace[i]:x16}"); errorReport.AppendLine($"\t\t0x{stackTraceSpan[i]:x16}");
} }
} }
errorReport.AppendLine("\tRegisters:"); errorReport.AppendLine("\tRegisters:");
Span<uint> xSpan = cpuContext32.X.AsSpan();
for (int i = 0; i < cpuContext32.X.Length; i++) for (int i = 0; i < xSpan.Length; i++)
{ {
errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext32.X[i]:x16}"); errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{xSpan[i]:x16}");
} }
errorReport.AppendLine(); errorReport.AppendLine();

View File

@@ -94,6 +94,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
primaryIndex = PlayerIndex.Unknown; primaryIndex = PlayerIndex.Unknown;
configuredCount = 0; configuredCount = 0;
Span<NpadState> nPadsSpan = _device.Hid.SharedMemory.Npads.AsSpan();
for (int i = 0; i < MaxControllers; ++i) for (int i = 0; i < MaxControllers; ++i)
{ {
ControllerType npad = _configuredTypes[i]; ControllerType npad = _configuredTypes[i];
@@ -103,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
continue; continue;
} }
ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet; ControllerType currentType = (ControllerType)nPadsSpan[i].InternalState.StyleSet;
if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i]) if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
{ {

View File

@@ -23,12 +23,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{ {
newState.TouchesCount = points.Length; newState.TouchesCount = points.Length;
int pointsLength = Math.Min(points.Length, newState.Touches.Length); Span<TouchState> touchesSpan = newState.Touches.AsSpan();
int pointsLength = Math.Min(points.Length, touchesSpan.Length);
for (int i = 0; i < pointsLength; ++i) for (int i = 0; i < pointsLength; ++i)
{ {
TouchPoint pi = points[i]; TouchPoint pi = points[i];
newState.Touches[i] = new TouchState touchesSpan[i] = new TouchState
{ {
DeltaTime = newState.SamplingNumber, DeltaTime = newState.SamplingNumber,
Attribute = pi.Attribute, Attribute = pi.Attribute,

View File

@@ -39,9 +39,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
public ref AtomicStorage<T> GetCurrentAtomicEntryRef() public ref AtomicStorage<T> GetCurrentAtomicEntryRef()
{ {
ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), 1); ulong countAvailable = Math.Min(Math.Max(0, ReadCurrentCount()), 1);
if (countAvailaible == 0) if (countAvailable == 0)
{ {
_storage[0] = default; _storage[0] = default;
@@ -49,21 +49,23 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
} }
ulong index = ReadCurrentIndex(); ulong index = ReadCurrentIndex();
Span<AtomicStorage<T>> storageSpan = _storage.AsSpan();
while (true) while (true)
{ {
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible) % MaxEntries); int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailable) % MaxEntries);
ref AtomicStorage<T> result = ref _storage[inputEntryIndex]; ref AtomicStorage<T> result = ref storageSpan[inputEntryIndex];
ulong samplingNumber0 = result.ReadSamplingNumberAtomic(); ulong samplingNumber0 = result.ReadSamplingNumberAtomic();
ulong samplingNumber1 = result.ReadSamplingNumberAtomic(); ulong samplingNumber1 = result.ReadSamplingNumberAtomic();
if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1) if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1)
{ {
ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible); ulong tempCount = Math.Min(ReadCurrentCount(), countAvailable);
countAvailaible = Math.Min(tempCount, 1); countAvailable = Math.Min(tempCount, 1);
index = ReadCurrentIndex(); index = ReadCurrentIndex();
continue; continue;
@@ -91,15 +93,17 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
ulong index = ReadCurrentIndex(); ulong index = ReadCurrentIndex();
AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailaible]; AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailaible];
Span<AtomicStorage<T>> storageSpan = _storage.AsSpan();
for (ulong i = 0; i < countAvailaible; i++) for (ulong i = 0; i < countAvailaible; i++)
{ {
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries); int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries);
int outputEntryIndex = (int)(countAvailaible - i - 1); int outputEntryIndex = (int)(countAvailaible - i - 1);
ulong samplingNumber0 = _storage[inputEntryIndex].ReadSamplingNumberAtomic(); ulong samplingNumber0 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic();
result[outputEntryIndex] = _storage[inputEntryIndex]; result[outputEntryIndex] = storageSpan[inputEntryIndex];
ulong samplingNumber1 = _storage[inputEntryIndex].ReadSamplingNumberAtomic(); ulong samplingNumber1 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic();
if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1)) if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1))
{ {

View File

@@ -0,0 +1,23 @@
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
{
struct NpadCondition
{
#pragma warning disable CS0414 // Field is assigned but its value is never used
private uint _00;
private uint _04;
private NpadJoyHoldType _holdType;
private uint _0C;
#pragma warning restore CS0414 // Field is assigned but its value is never used
public static NpadCondition Create()
{
return new NpadCondition()
{
_00 = 0,
_04 = 1,
_holdType = NpadJoyHoldType.Horizontal,
_0C = 1,
};
}
}
}

View File

@@ -41,6 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
public NpadLarkType LarkTypeRight; public NpadLarkType LarkTypeRight;
public NpadLuciaType LuciaType; public NpadLuciaType LuciaType;
public uint Unknown43EC; public uint Unknown43EC;
public ulong SixAxisSensorPropertiesArray;
[StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)] [StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)]
private struct Reserved2Struct { } private struct Reserved2Struct { }

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