Compare commits

..

109 Commits

Author SHA1 Message Date
Coxxs
8e941e4a8f gdb: Cleanup (ryubing/ryujinx!171)
See merge request ryubing/ryujinx!171
2025-10-16 19:53:51 -05:00
Hack茶ん
9aacf9b37b Update Korean translation (ryubing/ryujinx!168)
See merge request ryubing/ryujinx!168
2025-10-16 19:45:14 -05:00
Bluey Enjoyer
2b159dbca8 AHHHHHHHHHHHHHH (ryubing/ryujinx!170)
See merge request ryubing/ryujinx!170
2025-10-16 19:43:56 -05:00
GreemDev
c33a97f01c gdb: Cleanup Debugger.cs
by moving the GDB command handlers and command processor out of the class and into their own
2025-10-16 17:32:04 -05:00
GreemDev
fdbdb05cb5 misc: Update Ryujinx.LibHac 2025-10-16 12:23:15 -05:00
Coxxs
7268acbfb4 gdb: Do not skip CheckInterrupt when gdb stub is enabled (ryubing/ryujinx!169)
See merge request ryubing/ryujinx!169
2025-10-16 07:49:41 -05:00
GreemDev
d4107ac05f UI: Add a startup flag to ignore new Amiibo file updates, useful for testing changes you intend on committing to Ryubing/Nfc.
Flag is `--local-only-amiibo`
2025-10-15 21:51:13 -05:00
LotP
1d409f7127 12 GiB heap support (ryubing/ryujinx!166)
See merge request ryubing/ryujinx!166
2025-10-15 15:37:13 -05:00
GreemDev
2434c55266 UI: Updater: Fix "No" opening the changelog and "Show Changelog" doing nothing (aka doing what "No" should be doing) 2025-10-14 18:38:56 -05:00
GreemDev
99126603ba UI: swap the UI reset checkbox text back to a sentence instead of title cased 2025-10-14 16:12:11 -05:00
GreemDev
a62716002e chore: move HasPtcCacheFiles & HasShaderCacheFiles into ApplicationData, instead of having the weird static dependency helpers 2025-10-14 16:09:51 -05:00
GreemDev
47559cd311 Revert game list rounding
The selected highlight was bugged

https://fs.ryujinx.app/40cl4Ih9RiOWLVi7e4lJsw.png
2025-10-14 15:59:02 -05:00
Coxxs
51584a083b Flush the error log before exit (ryubing/ryujinx!163)
See merge request ryubing/ryujinx!163
2025-10-13 17:40:15 -05:00
Coxxs
ceec9617ef gdb: Fix the crash that occurs when GDB is connected early (ryubing/ryujinx!159)
See merge request ryubing/ryujinx!159
2025-10-11 19:06:14 -05:00
Coxxs
1865be47cf gdb: Add monitor minidump command (ryubing/ryujinx!158)
See merge request ryubing/ryujinx!158
2025-10-11 10:01:30 -05:00
LotP
ef9810582a Sync thread name on Schedule (ryubing/ryujinx!157)
See merge request ryubing/ryujinx!157
2025-10-11 07:47:45 -05:00
KeatonTheBot
13878acdb2 Avoid lookup of invalid textures if pool did not change (ryubing/ryujinx!113)
See merge request ryubing/ryujinx!113
2025-10-11 02:56:13 -05:00
LotP
e2143d43bc SDK20 and REV15 support (ryubing/ryujinx!50)
See merge request ryubing/ryujinx!50
2025-10-11 02:11:39 -05:00
Astell
4444ecae41 fix super mario galaxy 0x25E00040 error (ryubing/ryujinx!155)
See merge request ryubing/ryujinx!155
2025-10-10 17:37:42 -05:00
Neo
4f5a236c21 UI: Settings → Interface Tab + General Settings (ryubing/ryujinx!154)
See merge request ryubing/ryujinx!154
2025-10-09 16:32:33 -05:00
Lorenzo
db31ff15c7 it_IT translation update (ryubing/ryujinx!148)
See merge request ryubing/ryujinx!148
2025-10-06 20:22:15 -05:00
Bluey Enjoyer
e70cd08c9a cleanup work for my last MR/PR (ryubing/ryujinx!153)
See merge request ryubing/ryujinx!153
2025-10-04 13:07:31 -05:00
Neo
60b9723df4 UI: Main Window + General (ryubing/ryujinx!150)
See merge request ryubing/ryujinx!150
2025-10-03 16:02:37 -05:00
KeatonTheBot
1900924a78 Fix push descriptors bugfix logic for Intel Arc on Linux 2025-10-01 21:58:22 -05:00
GreemDev
5fb0b5e7ec vulkan: Intel Arc on Linux also has the push descriptors bug. 2025-10-01 13:03:22 -05:00
Bluey Enjoyer
8d0e28ed9d New RPC images and compat updates (ryubing/ryujinx!151)
See merge request ryubing/ryujinx!151
2025-10-01 12:29:27 -05:00
Babib3l
a4eafd01f5 es_ES and fr_FR translation updates (ryubing/ryujinx!129)
See merge request ryubing/ryujinx!129
2025-09-28 14:58:32 -05:00
在中国的泰国青年_
f55aa87ab9 update thai language additional (ryubing/ryujinx!108)
See merge request ryubing/ryujinx!108
2025-09-27 23:27:56 -05:00
Mcost45
bb4f8d8749 Include SL/SR default bindings for single joycons (ryubing/ryujinx!149)
See merge request ryubing/ryujinx!149
2025-09-22 14:23:40 -05:00
Alula
a6cb681f10 feat: resolve real module names in HLE debugger (ryubing/ryujinx!147)
See merge request ryubing/ryujinx!147
2025-09-20 07:05:44 -05:00
GreemDev
00ff3e6b1b chore: CI: oops missed another zsync reference 2025-09-14 01:35:05 -05:00
GreemDev
12510a5396 chore: CI: actually remove zsync files this time
I'm blind, the previous commit failed CI since it still thought they were being generated
2025-09-14 00:37:30 -05:00
GreemDev
ea30a0ed24 chore: ci: Remove .zsync 2025-09-14 00:26:09 -05:00
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
GreemDev
bb06eb751b Revert "fix: Super Mario Party Jamboree audio renderer crashing"
This reverts commit c0c021c7a966e32ed39018f8ec00f9f373173b60.

This commit was useless, and submitted by a GDKchan-obsessed chronically online lunatic who has disrespected the maintainers of this fork due to petty disagreements of how we run our Discord server. This is my parting gift to you: Stay gone. I'd prefer this code the way it was, because then you didn't touch it.

For the record, this commit is literally useless. The behavioral outcome is functionally identical to before the commit.
2025-08-06 18:43:31 -05:00
LotP
5613d3f35d Memory Changes (ryubing/ryujinx!46)
See merge request ryubing/ryujinx!46
2025-08-06 15:57:08 -05:00
Coxxs
54d4d184f4 gdb: Improve stepping (ryubing/ryujinx!106)
See merge request ryubing/ryujinx!106
2025-08-05 14:51:51 -05:00
Coxxs
d22756f1bd Add GDB Stub (ryubing/ryujinx!71)
See merge request ryubing/ryujinx!71
2025-08-04 20:45:15 -05:00
Neo
324f18aa5f Tooltip Fix Pt.4 (ryubing/ryujinx!101)
See merge request ryubing/ryujinx!101
2025-08-03 04:39:56 -05:00
Neo
31870707cf Tooltip Fix Pt.3 (ryubing/ryujinx!96)
See merge request ryubing/ryujinx!96
2025-08-02 20:30:54 -05:00
Hack茶ん
fd6648e30a Update Korean translation (ryubing/ryujinx!100)
See merge request ryubing/ryujinx!100
2025-08-02 20:29:50 -05:00
Neo
bc6be4e088 Other Tooltips Pt.2 (ryubing/ryujinx!95)
See merge request ryubing/ryujinx!95
2025-08-02 05:01:59 -05:00
Neo
64a6494d90 GameListContext Menu Tooltips (ryubing/ryujinx!94)
See merge request ryubing/ryujinx!94
2025-08-02 04:51:32 -05:00
Neo
ddb8afa6f4 Edit compatibility.csv (ryubing/ryujinx!88)
See merge request ryubing/ryujinx!88
2025-08-02 04:33:29 -05:00
Neo
c2f4118b1f Minor locales.json Adjustments (ryubing/ryujinx!91)
See merge request ryubing/ryujinx!91
2025-08-02 04:21:52 -05:00
GreemDev
47aa2c1513 Comment AppImage builds
It randomly started erroring in GitHub actions with exit code 8 but only sometimes, and I don't have the patience to debug it. I don't even use linux lol
2025-07-28 19:34:21 -05:00
LotP
f3a2f59683 Nullify Locales (ryubing/ryujinx!83)
See merge request ryubing/ryujinx!83
2025-07-28 18:24:35 -05:00
Rondo
51bcb9e128 Changes to uk_UA (ryubing/ryujinx!84)
See merge request ryubing/ryujinx!84
2025-07-28 15:55:48 -05:00
Daenorth
dce5f0eb55 Edit TileIDs.cs (ryubing/ryujinx!81)
See merge request ryubing/ryujinx!81
2025-07-24 13:04:22 -05:00
Daenorth
f2eb3749f9 Edit compatibility.csv (ryubing/ryujinx!80)
See merge request ryubing/ryujinx!80
2025-07-24 06:48:56 -05:00
328 changed files with 15106 additions and 8342 deletions

View File

@@ -102,51 +102,46 @@ 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 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" pushd publish_appimage
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
# popd
# pushd publish_appimage
# mv Ryujinx.AppImage ../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"
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync shell: bash
# 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"
# shell: bash
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal

View File

@@ -96,48 +96,43 @@ jobs:
shell: bash shell: bash
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 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 BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
#
# export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync" pushd publish_appimage
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
# popd
# pushd publish_appimage
# mv Ryujinx.AppImage ../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"
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync shell: bash
# 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" />
@@ -40,13 +40,13 @@
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" /> <PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.116" /> <PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.117" />
<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" />
@@ -55,7 +55,6 @@
<PackageVersion Include="SkiaSharp" Version="2.88.9" /> <PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" /> <PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="Starscript.Net" Version="1.0.36" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" /> <PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
<PackageVersion Include="System.Management" Version="9.0.2" /> <PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@ cd "$ROOTDIR"
BUILDDIR=${BUILDDIR:-publish} BUILDDIR=${BUILDDIR:-publish}
OUTDIR=${OUTDIR:-publish_appimage} OUTDIR=${OUTDIR:-publish_appimage}
UFLAG=${UFLAG:-"gh-releases-zsync|Ryubing|ryujinx|latest|*-x64.AppImage.zsync"}
rm -rf AppDir rm -rf AppDir
mkdir -p AppDir/usr/bin mkdir -p AppDir/usr/bin
@@ -23,11 +22,5 @@ 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" \
AppDir "$OUTDIR"/Ryujinx.AppImage AppDir "$OUTDIR"/Ryujinx.AppImage
# Move zsync file needed for delta updates
if [ "$RELEASE" = "1" ]; then
mv ./*.AppImage.zsync "$OUTDIR"
fi

View File

@@ -188,6 +188,8 @@
01003DD00BFEE000,"Airheart - Tales of broken Wings",,playable,2021-02-26 15:20:27 01003DD00BFEE000,"Airheart - Tales of broken Wings",,playable,2021-02-26 15:20:27
01007F100DE52000,"Akane",nvdec,playable,2022-07-21 00:12:18 01007F100DE52000,"Akane",nvdec,playable,2022-07-21 00:12:18
01009A800F0C8000,"Akash: Path of the Five",gpu;nvdec,ingame,2020-12-14 22:33:12 01009A800F0C8000,"Akash: Path of the Five",gpu;nvdec,ingame,2020-12-14 22:33:12
01009E8012976000,"AKIBA'S TRIP: Hellbound & Debriefed",,playable,2025-07-30 23:22:47
0100D74019A0E000,"AKIBA'S TRIP: Undead & Undressed Director's Cut",,playable,2025-07-31 13:58:42
010053100B0EA000,"Akihabara - Feel the Rhythm Remixed",,playable,2021-02-22 14:39:35 010053100B0EA000,"Akihabara - Feel the Rhythm Remixed",,playable,2021-02-22 14:39:35
0100D4C00EE0C000,"Akuarium",slow,playable,2020-12-12 23:43:36 0100D4C00EE0C000,"Akuarium",slow,playable,2020-12-12 23:43:36
010026E00FEBE000,"Akuto: Showdown",,playable,2020-08-04 19:43:27 010026E00FEBE000,"Akuto: Showdown",,playable,2020-08-04 19:43:27
@@ -976,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
@@ -1095,6 +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
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
@@ -1172,6 +1175,7 @@
0100EB100AB42000,"FINAL FANTASY XII THE ZODIAC AGE",opengl;vulkan-backend-bug,playable,2024-08-11 07:01:54 0100EB100AB42000,"FINAL FANTASY XII THE ZODIAC AGE",opengl;vulkan-backend-bug,playable,2024-08-11 07:01:54
010068F00AA78000,"FINAL FANTASY XV POCKET EDITION HD",,playable,2021-01-05 17:52:08 010068F00AA78000,"FINAL FANTASY XV POCKET EDITION HD",,playable,2021-01-05 17:52:08
0100CE4010AAC000,"FINAL FANTASY® CRYSTAL CHRONICLES™ Remastered Edition",,playable,2023-04-02 23:39:12 0100CE4010AAC000,"FINAL FANTASY® CRYSTAL CHRONICLES™ Remastered Edition",,playable,2023-04-02 23:39:12
010038B015560000,FINAL FANTASY TACTICS - The Ivalice Chronicles,gpu,boots,2024-09-30 02:59:00
01001BA00AE4E000,"Final Light, The Prison",,playable,2020-07-31 21:48:44 01001BA00AE4E000,"Final Light, The Prison",,playable,2020-07-31 21:48:44
0100FF100FB68000,"Finding Teddy 2 : Definitive Edition",gpu,ingame,2024-04-19 16:51:33 0100FF100FB68000,"Finding Teddy 2 : Definitive Edition",gpu,ingame,2024-04-19 16:51:33
0100F4E013AAE000,"Fire & Water",,playable,2020-12-15 15:43:20 0100F4E013AAE000,"Fire & Water",,playable,2020-12-15 15:43:20
@@ -1240,6 +1244,8 @@
010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38 010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38
010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27 010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27
0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24 0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24
0100C4E018A24000,"FRONT MISSION 2: Remake",,playable,2025-07-30 12:11:23
01007E6019872000,"FRONT MISSION 3: Remake",,playable,2025-07-30 12:12:02
0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59 0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59
0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36 0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36
010038A007AA4000,"FruitFall Crush",,playable,2020-10-20 11:33:33 010038A007AA4000,"FruitFall Crush",,playable,2020-10-20 11:33:33
@@ -1435,6 +1441,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
@@ -1444,6 +1451,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
@@ -1518,6 +1526,7 @@
010095C016C14000,"Iridium",,playable,2022-08-05 23:19:53 010095C016C14000,"Iridium",,playable,2022-08-05 23:19:53
0100AD300B786000,"Iris School of Wizardry -Vinculum Hearts-",,playable,2022-12-05 13:11:15 0100AD300B786000,"Iris School of Wizardry -Vinculum Hearts-",,playable,2022-12-05 13:11:15
0100945012168000,"Iris.Fall",nvdec,playable,2022-10-18 13:40:22 0100945012168000,"Iris.Fall",nvdec,playable,2022-10-18 13:40:22
010059801B736000,"IronFall: Invasion",,playable,2025-07-30 11:42:30
01005270118D6000,"Iron Wings",slow,ingame,2022-08-07 08:32:57 01005270118D6000,"Iron Wings",slow,ingame,2022-08-07 08:32:57
01004DB003E6A000,"IRONCAST",,playable,2021-01-13 13:54:29 01004DB003E6A000,"IRONCAST",,playable,2021-01-13 13:54:29
0100E5700CD56000,"Irony Curtain: From Matryoshka with Love",,playable,2021-06-04 20:12:37 0100E5700CD56000,"Irony Curtain: From Matryoshka with Love",,playable,2021-06-04 20:12:37
@@ -1881,7 +1890,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
@@ -2263,6 +2272,7 @@
0100ABF008968000,"Pokémon Sword + Pokémon Sword Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-26 15:40:37 0100ABF008968000,"Pokémon Sword + Pokémon Sword Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-26 15:40:37
01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20 01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20
0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35 0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51 010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02 01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
01005D100807A000,"Pokémon™ Quest",,playable,2022-02-22 16:12:32 01005D100807A000,"Pokémon™ Quest",,playable,2022-02-22 16:12:32
@@ -2270,6 +2280,7 @@
01008F6008C5E000,"Pokémon™ Violet",gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug,ingame,2024-07-30 02:51:48 01008F6008C5E000,"Pokémon™ Violet",gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug,ingame,2024-07-30 02:51:48
0100187003A36000,"Pokémon™: Lets Go, Eevee!",crash;nvdec;online-broken;ldn-broken,ingame,2024-06-01 15:03:04 0100187003A36000,"Pokémon™: Lets Go, Eevee!",crash;nvdec;online-broken;ldn-broken,ingame,2024-06-01 15:03:04
010003F003A34000,"Pokémon™: Lets Go, Pikachu!",crash;nvdec;online-broken;ldn-broken,ingame,2024-03-15 07:55:41 010003F003A34000,"Pokémon™: Lets Go, Pikachu!",crash;nvdec;online-broken;ldn-broken,ingame,2024-03-15 07:55:41
0100F43008C44000,"Pokémon Legends: Z-A",gpu;crash;ldn-broken,ingame,2025-10-16 19:13:00
0100B3F000BE2000,"Pokkén Tournament™ DX",nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug,playable,2024-07-18 23:11:08 0100B3F000BE2000,"Pokkén Tournament™ DX",nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug,playable,2024-07-18 23:11:08
010030D005AE6000,"Pokkén Tournament™ DX Demo",demo;opengl-backend-bug,playable,2022-08-10 12:03:19 010030D005AE6000,"Pokkén Tournament™ DX Demo",demo;opengl-backend-bug,playable,2022-08-10 12:03:19
0100A3500B4EC000,"Polandball: Can Into Space",,playable,2020-06-25 15:13:26 0100A3500B4EC000,"Polandball: Can Into Space",,playable,2020-06-25 15:13:26
@@ -2306,6 +2317,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
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
@@ -2571,6 +2583,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
@@ -2697,6 +2710,8 @@
01008F701C074000,"SONIC SUPERSTARS",gpu;nvdec,ingame,2023-10-28 17:48:07 01008F701C074000,"SONIC SUPERSTARS",gpu;nvdec,ingame,2023-10-28 17:48:07
010088801C150000,"Sonic Superstars Digital Art Book with Mini Digital Soundtrack",,playable,2024-08-20 13:26:56 010088801C150000,"Sonic Superstars Digital Art Book with Mini Digital Soundtrack",,playable,2024-08-20 13:26:56
01005EA01C0FC000,"SONIC X SHADOW GENERATIONS",crash,ingame,2025-01-07 04:20:45 01005EA01C0FC000,"SONIC X SHADOW GENERATIONS",crash,ingame,2025-01-07 04:20:45
010064B0242BE000,"Sonic Racing: CrossWorlds - Demo",gpu;vulkan-backend-bug;demo,ingame,2024-09-25 11:27:53
01006E001823C000,"Sonic Racing: CrossWorlds",gpu;vulkan-backend-bug;ldn-broken,ingame,2024-09-30 17:23:00
010064F00C212000,"Soul Axiom Rebooted",nvdec;slow,ingame,2020-09-04 12:41:01 010064F00C212000,"Soul Axiom Rebooted",nvdec;slow,ingame,2020-09-04 12:41:01
0100F2100F0B2000,"Soul Searching",,playable,2020-07-09 18:39:07 0100F2100F0B2000,"Soul Searching",,playable,2020-07-09 18:39:07
01008F2005154000,"South Park™: The Fractured but Whole™ - Standard Edition",slow;online-broken;vulkan-backend-bug;gpu,ingame,2025-01-21 17:35:10 01008F2005154000,"South Park™: The Fractured but Whole™ - Standard Edition",slow;online-broken;vulkan-backend-bug;gpu,ingame,2025-01-21 17:35:10
@@ -2768,7 +2783,7 @@
0100E6B0115FC000,"Star99",online,menus,2021-11-26 14:18:51 0100E6B0115FC000,"Star99",online,menus,2021-11-26 14:18:51
01002100137BA000,"Stardash",,playable,2021-01-21 16:31:19 01002100137BA000,"Stardash",,playable,2021-01-21 16:31:19
0100E65002BB8000,"Stardew Valley",online-broken;ldn-untested,playable,2024-02-14 03:11:19 0100E65002BB8000,"Stardew Valley",online-broken;ldn-untested,playable,2024-02-14 03:11:19
01002CC003FE6000,"Starlink: Battle for Atlas™ Digital Edition",services-horizon;crash;Needs Update,nothing,2024-05-05 17:25:11 01002CC003FE6000,"Starlink: Battle for Atlas™ Digital Edition",,playable,2025-07-30 12:09:37
010098E010FDA000,"Starlit Adventures Golden Stars",,playable,2020-11-21 12:14:43 010098E010FDA000,"Starlit Adventures Golden Stars",,playable,2020-11-21 12:14:43
01001BB00AC26000,"STARSHIP AVENGER Operation: Take Back Earth",,playable,2021-01-12 15:52:55 01001BB00AC26000,"STARSHIP AVENGER Operation: Take Back Earth",,playable,2021-01-12 15:52:55
010000700A572000,"State of Anarchy: Master of Mayhem",nvdec,playable,2021-01-12 19:00:05 010000700A572000,"State of Anarchy: Master of Mayhem",nvdec,playable,2021-01-12 19:00:05
@@ -2854,11 +2869,13 @@
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42 0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31 ,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16 010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
010099C022B96000,"Super Mario Galaxy",slow;vulkan;amd-vendor-bug;vulkan-vendor-bug,ingame,2025-10-01 15:30:00
0100FD8022DAA000,"Super Mario Galaxy 2",slow;vulkan;amd-vendor-bug;vulkan-vendor-bug;deadlock,ingame,2025-10-04 18:50:00
010028600EBDA000,"Super Mario™ 3D World + Bowsers Fury",ldn-works,playable,2024-07-31 10:45:37 010028600EBDA000,"Super Mario™ 3D World + Bowsers Fury",ldn-works,playable,2024-07-31 10:45:37
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07 01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
01009C200D60E000,"Super Meat Boy Forever",gpu,boots,2021-04-26 14:25:39 01009C200D60E000,"Super Meat Boy Forever",gpu,boots,2021-04-26 14:25:39
0100BDD00EC5C000,"Super Mega Space Blaster Special Turbo",online,playable,2020-08-06 12:13:25 0100BDD00EC5C000,"Super Mega Space Blaster Special Turbo",online,playable,2020-08-06 12:13:25
010031F019294000,"Super Monkey Ball Banana Rumble",,playable,2024-06-28 10:39:18 010031F019294000,"Super Monkey Ball Banana Rumble",ldn-broken,playable,2025-10-01 18:03:00
0100B2A00E1E0000,"Super Monkey Ball: Banana Blitz HD",online-broken,playable,2022-09-16 13:16:25 0100B2A00E1E0000,"Super Monkey Ball: Banana Blitz HD",online-broken,playable,2022-09-16 13:16:25
01006D000D2A0000,"Super Mutant Alien Assault",,playable,2020-06-07 23:32:45 01006D000D2A0000,"Super Mutant Alien Assault",,playable,2020-06-07 23:32:45
01004D600AC14000,"Super Neptunia RPG",nvdec,playable,2022-08-17 16:38:52 01004D600AC14000,"Super Neptunia RPG",nvdec,playable,2022-08-17 16:38:52
@@ -2924,6 +2941,7 @@
0100B76011DAA000,"Taxi Chaos",slow;online-broken;UE4,playable,2022-10-25 19:13:00 0100B76011DAA000,"Taxi Chaos",slow;online-broken;UE4,playable,2022-10-25 19:13:00
0100F43011E5A000,"Tcheco in the Castle of Lucio",,playable,2020-06-27 13:35:43 0100F43011E5A000,"Tcheco in the Castle of Lucio",,playable,2020-06-27 13:35:43
010092B0091D0000,"Team Sonic Racing",online-broken;ldn-works,playable,2024-02-05 15:05:27 010092B0091D0000,"Team Sonic Racing",online-broken;ldn-works,playable,2024-02-05 15:05:27
010084B00B36E000,"Team Sonic Racing",online-broken;ldn-works,playable,2024-02-05 15:05:27
0100FE701475A000,"Teenage Mutant Ninja Turtles: Shredder's Revenge",deadlock;crash,boots,2024-09-28 09:31:39 0100FE701475A000,"Teenage Mutant Ninja Turtles: Shredder's Revenge",deadlock;crash,boots,2024-09-28 09:31:39
01005CF01E784000,"Teenage Mutant Ninja Turtles: Splintered Fate",,playable,2024-08-03 13:50:42 01005CF01E784000,"Teenage Mutant Ninja Turtles: Splintered Fate",,playable,2024-08-03 13:50:42
0100FDB0154E4000,"Teenage Mutant Ninja Turtles: The Cowabunga Collection",,playable,2024-01-22 19:39:04 0100FDB0154E4000,"Teenage Mutant Ninja Turtles: The Cowabunga Collection",,playable,2024-01-22 19:39:04
@@ -2969,6 +2987,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
@@ -2976,6 +2995,7 @@
0100C2E0129A6000,"The Executioner",nvdec,playable,2021-01-23 00:31:28 0100C2E0129A6000,"The Executioner",nvdec,playable,2021-01-23 00:31:28
01006050114D4000,"The Experiment: Escape Room",gpu,ingame,2022-09-30 13:20:35 01006050114D4000,"The Experiment: Escape Room",gpu,ingame,2022-09-30 13:20:35
0100B5900DFB2000,"The Eyes of Ara",,playable,2022-09-16 14:44:06 0100B5900DFB2000,"The Eyes of Ara",,playable,2022-09-16 14:44:06
0100BA5013E52000,"The Falconeer: Warrior Edition",,playable,2025-07-30 12:04:50
01002DD00AF9E000,"The Fall",gpu,ingame,2020-05-31 23:31:16 01002DD00AF9E000,"The Fall",gpu,ingame,2020-05-31 23:31:16
01003E5002320000,"The Fall Part 2: Unbound",,playable,2021-11-06 02:18:08 01003E5002320000,"The Fall Part 2: Unbound",,playable,2021-11-06 02:18:08
0100CDC00789E000,"The Final Station",nvdec,playable,2022-08-22 15:54:39 0100CDC00789E000,"The Final Station",nvdec,playable,2022-08-22 15:54:39
@@ -3198,6 +3218,7 @@
010000400F582000,"TT Isle of Man Ride on the Edge 2",gpu;nvdec;online-broken,ingame,2022-09-30 22:13:05 010000400F582000,"TT Isle of Man Ride on the Edge 2",gpu;nvdec;online-broken,ingame,2022-09-30 22:13:05
0100752011628000,"TTV2",,playable,2020-11-27 13:21:36 0100752011628000,"TTV2",,playable,2020-11-27 13:21:36
0100AFE00452E000,"Tumblestone",,playable,2021-01-07 17:49:20 0100AFE00452E000,"Tumblestone",,playable,2021-01-07 17:49:20
0100D1A01D7BA000,"Turbo Overkill",,playable,2025-07-30 12:08:57
010085500D5F6000,"Turok",gpu,ingame,2021-06-04 13:16:24 010085500D5F6000,"Turok",gpu,ingame,2021-06-04 13:16:24
0100CDC00D8D6000,"Turok 2: Seeds of Evil",gpu;vulkan,ingame,2022-09-12 17:50:05 0100CDC00D8D6000,"Turok 2: Seeds of Evil",gpu;vulkan,ingame,2022-09-12 17:50:05
010004B0130C8000,"Turrican Flashback",audout,playable,2021-08-30 10:07:56 010004B0130C8000,"Turrican Flashback",audout,playable,2021-08-30 10:07:56
@@ -3207,6 +3228,7 @@
010038400C2FE000,"TY the Tasmanian Tiger™ HD",32-bit;crash;nvdec,menus,2020-12-17 21:15:00 010038400C2FE000,"TY the Tasmanian Tiger™ HD",32-bit;crash;nvdec,menus,2020-12-17 21:15:00
010073A00C4B2000,"Tyd wag vir Niemand",,playable,2021-03-02 13:39:53 010073A00C4B2000,"Tyd wag vir Niemand",,playable,2021-03-02 13:39:53
0100D5B00D6DA000,"Type:Rider",,playable,2021-01-06 13:12:55 0100D5B00D6DA000,"Type:Rider",,playable,2021-01-06 13:12:55
01008AF01AD22000,"The Patrick Star Game",,playable,2025-10-01 17:55:30
010040D01222C000,"UBERMOSH: SANTICIDE",,playable,2020-11-27 15:05:01 010040D01222C000,"UBERMOSH: SANTICIDE",,playable,2020-11-27 15:05:01
0100992010BF8000,"Ubongo",,playable,2021-02-04 21:15:01 0100992010BF8000,"Ubongo",,playable,2021-02-04 21:15:01
010079000B56C000,"UglyDolls: An Imperfect Adventure",nvdec;UE4,playable,2022-08-25 14:42:16 010079000B56C000,"UglyDolls: An Imperfect Adventure",nvdec;UE4,playable,2022-08-25 14:42:16
@@ -3221,6 +3243,8 @@
0100592005164000,"UNBOX: Newbie's Adventure",UE4,playable,2022-08-29 13:12:56 0100592005164000,"UNBOX: Newbie's Adventure",UE4,playable,2022-08-29 13:12:56
01002D900C5E4000,"Uncanny Valley",nvdec,playable,2021-06-04 13:28:45 01002D900C5E4000,"Uncanny Valley",nvdec,playable,2021-06-04 13:28:45
010076F011F54000,"Undead & Beyond",nvdec,playable,2022-10-04 09:11:18 010076F011F54000,"Undead & Beyond",nvdec,playable,2022-10-04 09:11:18
01009B700D0B8000,"Undead Horde",,playable,2025-07-30 12:05:05
0100FC301A878000,"Undead Horde 2: Necropolis",,playable,2025-07-30 12:06:07
01008F3013E4E000,"Under Leaves",,playable,2021-05-22 18:13:58 01008F3013E4E000,"Under Leaves",,playable,2021-05-22 18:13:58
010080B00AD66000,"Undertale",,playable,2022-08-31 17:31:46 010080B00AD66000,"Undertale",,playable,2022-08-31 17:31:46
01008F80049C6000,"Unepic",,playable,2024-01-15 17:03:00 01008F80049C6000,"Unepic",,playable,2024-01-15 17:03:00
@@ -3451,3 +3475,6 @@
0100F4401940A000,"超探偵事件簿 レインコード (Master Detective Archives: Rain Code)",crash,ingame,2024-02-12 20:58:31 0100F4401940A000,"超探偵事件簿 レインコード (Master Detective Archives: Rain Code)",crash,ingame,2024-02-12 20:58:31
010064801A01C000,"超次元ゲイム ネプテューヌ GameMaker R:Evolution",crash,nothing,2023-10-30 22:37:40 010064801A01C000,"超次元ゲイム ネプテューヌ GameMaker R:Evolution",crash,nothing,2023-10-30 22:37:40
0100F3400332C000,"ブレイド2",deadlock;amd-vendor-bug,ingame,2024-03-28 14:31:41 0100F3400332C000,"ブレイド2",deadlock;amd-vendor-bug,ingame,2024-03-28 14:31:41
010075000ECBE000,"超级马力欧 奥德赛",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
010075100E8EC000,"马力欧卡丁车8 豪华版",32-bit;ldn-works;LAN;amd-vendor-bug,playable,2024-09-19 11:55:17
0100E8C00F506000,"新 超级马力欧兄弟U 豪华版",32-bit,playable,2023-10-08 02:06:37
1 title_id game_name labels status last_updated
188 01003DD00BFEE000 Airheart - Tales of broken Wings playable 2021-02-26 15:20:27
189 01007F100DE52000 Akane nvdec playable 2022-07-21 00:12:18
190 01009A800F0C8000 Akash: Path of the Five gpu;nvdec ingame 2020-12-14 22:33:12
191 01009E8012976000 AKIBA'S TRIP: Hellbound & Debriefed playable 2025-07-30 23:22:47
192 0100D74019A0E000 AKIBA'S TRIP: Undead & Undressed Director's Cut playable 2025-07-31 13:58:42
193 010053100B0EA000 Akihabara - Feel the Rhythm Remixed playable 2021-02-22 14:39:35
194 0100D4C00EE0C000 Akuarium slow playable 2020-12-12 23:43:36
195 010026E00FEBE000 Akuto: Showdown playable 2020-08-04 19:43:27
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 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
1175 0100EB100AB42000 FINAL FANTASY XII THE ZODIAC AGE opengl;vulkan-backend-bug playable 2024-08-11 07:01:54
1176 010068F00AA78000 FINAL FANTASY XV POCKET EDITION HD playable 2021-01-05 17:52:08
1177 0100CE4010AAC000 FINAL FANTASY® CRYSTAL CHRONICLES™ Remastered Edition playable 2023-04-02 23:39:12
1178 010038B015560000 FINAL FANTASY TACTICS - The Ivalice Chronicles gpu boots 2024-09-30 02:59:00
1179 01001BA00AE4E000 Final Light, The Prison playable 2020-07-31 21:48:44
1180 0100FF100FB68000 Finding Teddy 2 : Definitive Edition gpu ingame 2024-04-19 16:51:33
1181 0100F4E013AAE000 Fire & Water playable 2020-12-15 15:43:20
1244 010003F00BD48000 Friday the 13th: Killer Puzzle playable 2021-01-28 01:33:38
1245 010092A00C4B6000 Friday the 13th: The Game Ultimate Slasher Edition nvdec;online-broken;UE4 playable 2022-09-06 17:33:27
1246 0100F200178F4000 FRONT MISSION 1st: Remake playable 2023-06-09 07:44:24
1247 0100C4E018A24000 FRONT MISSION 2: Remake playable 2025-07-30 12:11:23
1248 01007E6019872000 FRONT MISSION 3: Remake playable 2025-07-30 12:12:02
1249 0100861012474000 Frontline Zed playable 2020-10-03 12:55:59
1250 0100B5300B49A000 Frost playable 2022-07-27 12:00:36
1251 010038A007AA4000 FruitFall Crush playable 2020-10-20 11:33:33
1441 0100C2700E338000 Heroland playable 2020-08-05 15:35:39
1442 01007AC00E012000 HexaGravity playable 2021-05-28 13:47:48
1443 01004E800F03C000 Hidden slow ingame 2022-10-05 10:56:53
1444 0100C1101EE5A000 High on Life menus 2025-08-26 19:11:00
1445 0100F6A00A684000 Higurashi no Naku Koro ni Hō audio ingame 2021-09-18 14:40:28
1446 0100F8D0129F4000 Himehibi 1 gakki - Princess Days crash nothing 2021-11-03 08:34:19
1447 0100F3D008436000 Hiragana Pixel Party playable 2021-01-14 08:36:50
1451 0100F7300ED2C000 Hoggy2 playable 2022-10-10 13:53:35
1452 0100F7E00C70E000 Hogwarts Legacy UE4;slow ingame 2024-09-03 19:53:58
1453 0100633007D48000 Hollow Knight nvdec playable 2023-01-16 15:44:56
1454 010013C00E930000 Hollow Knight: Silksong playable 2025-09-04 17:23:22
1455 0100F2100061E800 Hollow0 UE4;gpu ingame 2021-03-03 23:42:56
1456 0100342009E16000 Holy Potatoes! What The Hell?! playable 2020-07-03 10:48:56
1457 010071B00C904000 HoPiKo playable 2021-01-13 20:12:38
1526 010095C016C14000 Iridium playable 2022-08-05 23:19:53
1527 0100AD300B786000 Iris School of Wizardry -Vinculum Hearts- playable 2022-12-05 13:11:15
1528 0100945012168000 Iris.Fall nvdec playable 2022-10-18 13:40:22
1529 010059801B736000 IronFall: Invasion playable 2025-07-30 11:42:30
1530 01005270118D6000 Iron Wings slow ingame 2022-08-07 08:32:57
1531 01004DB003E6A000 IRONCAST playable 2021-01-13 13:54:29
1532 0100E5700CD56000 Irony Curtain: From Matryoshka with Love playable 2021-06-04 20:12:37
1890 010097800EA20000 Monster Energy Supercross - The Official Videogame 3 UE4;audout;nvdec;online playable 2021-06-14 12:37:54
1891 0100E9900ED74000 Monster Farm 32-bit;nvdec playable 2021-05-05 19:29:13
1892 0100770008DD8000 Monster Hunter Generations Ultimate™ 32-bit;online-broken;ldn-works playable 2024-03-18 14:35:36
1893 0100B04011742000 Monster Hunter Rise MONSTER HUNTER RISE gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works ingame 2024-08-24 11:04:59
1894 010093A01305C000 Monster Hunter Rise Demo online-broken;ldn-works;demo playable 2022-10-18 23:04:17
1895 0100E21011446000 Monster Hunter Stories 2: Wings of Ruin services ingame 2022-07-10 19:27:30
1896 010042501329E000 MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version demo playable 2022-11-13 22:20:26
2272 0100ABF008968000 Pokémon Sword + Pokémon Sword Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-26 15:40:37
2273 01009AD008C4C000 Pokémon: Let's Go, Pikachu! demo slow;demo playable 2023-11-26 11:23:20
2274 0100000011D90000 Pokémon™ Brilliant Diamond gpu;ldn-works ingame 2024-08-28 13:26:35
2275 010018E011D92000 Pokémon™ Shining Pearl gpu;ldn-works ingame 2024-08-28 13:26:35
2276 010015F008C54000 Pokémon™ HOME Needs Update;crash;services menus 2020-12-06 06:01:51
2277 01001F5010DFA000 Pokémon™ Legends: Arceus gpu;Needs Update;ldn-works ingame 2024-09-19 10:02:02
2278 01005D100807A000 Pokémon™ Quest playable 2022-02-22 16:12:32
2280 01008F6008C5E000 Pokémon™ Violet gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug ingame 2024-07-30 02:51:48
2281 0100187003A36000 Pokémon™: Let’s Go, Eevee! crash;nvdec;online-broken;ldn-broken ingame 2024-06-01 15:03:04
2282 010003F003A34000 Pokémon™: Let’s Go, Pikachu! crash;nvdec;online-broken;ldn-broken ingame 2024-03-15 07:55:41
2283 0100F43008C44000 Pokémon Legends: Z-A gpu;crash;ldn-broken ingame 2025-10-16 19:13:00
2284 0100B3F000BE2000 Pokkén Tournament™ DX nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug playable 2024-07-18 23:11:08
2285 010030D005AE6000 Pokkén Tournament™ DX Demo demo;opengl-backend-bug playable 2022-08-10 12:03:19
2286 0100A3500B4EC000 Polandball: Can Into Space playable 2020-06-25 15:13:26
2317 010077B00BDD8000 Professional Farmer: Nintendo Switch™ Edition slow playable 2020-12-16 13:38:19
2318 010018300C83A000 Professor Lupo and his Horrible Pets playable 2020-06-12 00:08:45
2319 0100D1F0132F6000 Professor Lupo: Ocean playable 2021-04-14 16:33:33
2320 0100C3A017834000 Prodeus playable 2025-07-30 12:07:52
2321 0100BBD00976C000 Project Highrise: Architect's Edition playable 2022-08-10 17:19:12
2322 0100ACE00DAB6000 Project Nimbus: Complete Edition nvdec;UE4;vulkan-backend-bug playable 2022-08-10 17:35:43
2323 01002980140F6000 Project TRIANGLE STRATEGY™ Debut Demo UE4;demo playable 2022-10-24 21:40:27
2583 0100C610154CA000 Shadowrun: Hong Kong - Extended Edition gpu;Needs Update ingame 2022-10-04 20:53:09
2584 010000000EEF0000 Shadows 2: Perfidia playable 2020-08-07 12:43:46
2585 0100AD700CBBE000 Shadows of Adam playable 2021-01-11 13:35:58
2586 010037A01F96C000 Shadows of the Damned: Hella Remastered playable 2025-09-05 11:34:32
2587 01002A800C064000 Shadowverse Champions Battle playable 2022-10-02 22:59:29
2588 01003B90136DA000 Shadowverse: Champion's Battle crash nothing 2023-03-06 00:31:50
2589 0100820013612000 Shady Part of Me playable 2022-10-20 11:31:55
2710 01008F701C074000 SONIC SUPERSTARS gpu;nvdec ingame 2023-10-28 17:48:07
2711 010088801C150000 Sonic Superstars Digital Art Book with Mini Digital Soundtrack playable 2024-08-20 13:26:56
2712 01005EA01C0FC000 SONIC X SHADOW GENERATIONS crash ingame 2025-01-07 04:20:45
2713 010064B0242BE000 Sonic Racing: CrossWorlds - Demo gpu;vulkan-backend-bug;demo ingame 2024-09-25 11:27:53
2714 01006E001823C000 Sonic Racing: CrossWorlds gpu;vulkan-backend-bug;ldn-broken ingame 2024-09-30 17:23:00
2715 010064F00C212000 Soul Axiom Rebooted nvdec;slow ingame 2020-09-04 12:41:01
2716 0100F2100F0B2000 Soul Searching playable 2020-07-09 18:39:07
2717 01008F2005154000 South Park™: The Fractured but Whole™ - Standard Edition slow;online-broken;vulkan-backend-bug;gpu ingame 2025-01-21 17:35:10
2783 0100E6B0115FC000 Star99 online menus 2021-11-26 14:18:51
2784 01002100137BA000 Stardash playable 2021-01-21 16:31:19
2785 0100E65002BB8000 Stardew Valley online-broken;ldn-untested playable 2024-02-14 03:11:19
2786 01002CC003FE6000 Starlink: Battle for Atlas™ Digital Edition services-horizon;crash;Needs Update nothing playable 2024-05-05 17:25:11 2025-07-30 12:09:37
2787 010098E010FDA000 Starlit Adventures Golden Stars playable 2020-11-21 12:14:43
2788 01001BB00AC26000 STARSHIP AVENGER Operation: Take Back Earth playable 2021-01-12 15:52:55
2789 010000700A572000 State of Anarchy: Master of Mayhem nvdec playable 2021-01-12 19:00:05
2869 0100BC0018138000 Super Mario RPG™ gpu;audio;nvdec ingame 2024-06-19 17:43:42
2870 Super Mario World homebrew boots 2024-06-13 01:40:31
2871 010049900F546000 Super Mario™ 3D All-Stars services-horizon;slow;vulkan;amd-vendor-bug ingame 2024-05-07 02:38:16
2872 010099C022B96000 Super Mario Galaxy slow;vulkan;amd-vendor-bug;vulkan-vendor-bug ingame 2025-10-01 15:30:00
2873 0100FD8022DAA000 Super Mario Galaxy 2 slow;vulkan;amd-vendor-bug;vulkan-vendor-bug;deadlock ingame 2025-10-04 18:50:00
2874 010028600EBDA000 Super Mario™ 3D World + Bowser’s Fury ldn-works playable 2024-07-31 10:45:37
2875 01004F8006A78000 Super Meat Boy services playable 2020-04-02 23:10:07
2876 01009C200D60E000 Super Meat Boy Forever gpu boots 2021-04-26 14:25:39
2877 0100BDD00EC5C000 Super Mega Space Blaster Special Turbo online playable 2020-08-06 12:13:25
2878 010031F019294000 Super Monkey Ball Banana Rumble ldn-broken playable 2024-06-28 10:39:18 2025-10-01 18:03:00
2879 0100B2A00E1E0000 Super Monkey Ball: Banana Blitz HD online-broken playable 2022-09-16 13:16:25
2880 01006D000D2A0000 Super Mutant Alien Assault playable 2020-06-07 23:32:45
2881 01004D600AC14000 Super Neptunia RPG nvdec playable 2022-08-17 16:38:52
2941 0100B76011DAA000 Taxi Chaos slow;online-broken;UE4 playable 2022-10-25 19:13:00
2942 0100F43011E5A000 Tcheco in the Castle of Lucio playable 2020-06-27 13:35:43
2943 010092B0091D0000 Team Sonic Racing online-broken;ldn-works playable 2024-02-05 15:05:27
2944 010084B00B36E000 Team Sonic Racing online-broken;ldn-works playable 2024-02-05 15:05:27
2945 0100FE701475A000 Teenage Mutant Ninja Turtles: Shredder's Revenge deadlock;crash boots 2024-09-28 09:31:39
2946 01005CF01E784000 Teenage Mutant Ninja Turtles: Splintered Fate playable 2024-08-03 13:50:42
2947 0100FDB0154E4000 Teenage Mutant Ninja Turtles: The Cowabunga Collection playable 2024-01-22 19:39:04
2987 0100EBA01548E000 The Cruel King and the Great Hero gpu;services ingame 2022-12-02 07:02:08
2988 010051800E922000 The Dark Crystal: Age of Resistance Tactics playable 2020-08-11 13:43:41
2989 01003DE00918E000 The Darkside Detective playable 2020-06-03 22:16:18
2990 010032B015D66000 The DioField Chronicle playable 2025-09-05 11:35:50
2991 01000A10041EA000 The Elder Scrolls V: Skyrim gpu;crash ingame 2024-07-14 03:21:31
2992 01004A9006B84000 The End Is Nigh playable 2020-06-01 11:26:45
2993 0100CA100489C000 The Escapists 2 nvdec playable 2020-09-24 12:31:31
2995 0100C2E0129A6000 The Executioner nvdec playable 2021-01-23 00:31:28
2996 01006050114D4000 The Experiment: Escape Room gpu ingame 2022-09-30 13:20:35
2997 0100B5900DFB2000 The Eyes of Ara playable 2022-09-16 14:44:06
2998 0100BA5013E52000 The Falconeer: Warrior Edition playable 2025-07-30 12:04:50
2999 01002DD00AF9E000 The Fall gpu ingame 2020-05-31 23:31:16
3000 01003E5002320000 The Fall Part 2: Unbound playable 2021-11-06 02:18:08
3001 0100CDC00789E000 The Final Station nvdec playable 2022-08-22 15:54:39
3218 010000400F582000 TT Isle of Man Ride on the Edge 2 gpu;nvdec;online-broken ingame 2022-09-30 22:13:05
3219 0100752011628000 TTV2 playable 2020-11-27 13:21:36
3220 0100AFE00452E000 Tumblestone playable 2021-01-07 17:49:20
3221 0100D1A01D7BA000 Turbo Overkill playable 2025-07-30 12:08:57
3222 010085500D5F6000 Turok gpu ingame 2021-06-04 13:16:24
3223 0100CDC00D8D6000 Turok 2: Seeds of Evil gpu;vulkan ingame 2022-09-12 17:50:05
3224 010004B0130C8000 Turrican Flashback audout playable 2021-08-30 10:07:56
3228 010038400C2FE000 TY the Tasmanian Tiger™ HD 32-bit;crash;nvdec menus 2020-12-17 21:15:00
3229 010073A00C4B2000 Tyd wag vir Niemand playable 2021-03-02 13:39:53
3230 0100D5B00D6DA000 Type:Rider playable 2021-01-06 13:12:55
3231 01008AF01AD22000 The Patrick Star Game playable 2025-10-01 17:55:30
3232 010040D01222C000 UBERMOSH: SANTICIDE playable 2020-11-27 15:05:01
3233 0100992010BF8000 Ubongo playable 2021-02-04 21:15:01
3234 010079000B56C000 UglyDolls: An Imperfect Adventure nvdec;UE4 playable 2022-08-25 14:42:16
3243 0100592005164000 UNBOX: Newbie's Adventure UE4 playable 2022-08-29 13:12:56
3244 01002D900C5E4000 Uncanny Valley nvdec playable 2021-06-04 13:28:45
3245 010076F011F54000 Undead & Beyond nvdec playable 2022-10-04 09:11:18
3246 01009B700D0B8000 Undead Horde playable 2025-07-30 12:05:05
3247 0100FC301A878000 Undead Horde 2: Necropolis playable 2025-07-30 12:06:07
3248 01008F3013E4E000 Under Leaves playable 2021-05-22 18:13:58
3249 010080B00AD66000 Undertale playable 2022-08-31 17:31:46
3250 01008F80049C6000 Unepic playable 2024-01-15 17:03:00
3475 0100F4401940A000 超探偵事件簿 レインコード (Master Detective Archives: Rain Code) crash ingame 2024-02-12 20:58:31
3476 010064801A01C000 超次元ゲイム ネプテューヌ GameMaker R:Evolution crash nothing 2023-10-30 22:37:40
3477 0100F3400332C000 ゼノブレイド2 deadlock;amd-vendor-bug ingame 2024-03-28 14:31:41
3478 010075000ECBE000 超级马力欧 奥德赛 nvdec;intel-vendor-bug;mac-bug playable 2024-08-25 01:32:34
3479 010075100E8EC000 马力欧卡丁车8 豪华版 32-bit;ldn-works;LAN;amd-vendor-bug playable 2024-09-19 11:55:17
3480 0100E8C00F506000 新 超级马力欧兄弟U 豪华版 32-bit playable 2023-10-08 02:06:37

View File

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

View File

@@ -3,6 +3,7 @@ using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using ExecutionContext = ARMeilleure.State.ExecutionContext;
namespace ARMeilleure.Instructions namespace ARMeilleure.Instructions
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,11 @@
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Common namespace Ryujinx.Audio.Renderer.Common
{ {
/// <summary> /// <summary>
/// Represents the input parameter for <see cref="Server.BehaviourContext"/>. /// Represents the input parameter for <see cref="BehaviourInfo"/>.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BehaviourParameter public struct BehaviourParameter
@@ -21,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common
/// <summary> /// <summary>
/// The flags given controlling behaviour of the audio renderer /// The flags given controlling behaviour of the audio renderer
/// </summary> /// </summary>
/// <remarks>See <see cref="Server.BehaviourContext.UpdateFlags(ulong)"/> and <see cref="Server.BehaviourContext.IsMemoryPoolForceMappingEnabled"/>.</remarks> /// <remarks>See <see cref="BehaviourInfo.UpdateFlags(ulong)"/> and <see cref="BehaviourInfo.IsMemoryPoolForceMappingEnabled"/>.</remarks>
public ulong Flags; public ulong Flags;
/// <summary> /// <summary>
@@ -43,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Common
/// <summary> /// <summary>
/// Extra information given with the <see cref="ResultCode"/> /// Extra information given with the <see cref="ResultCode"/>
/// </summary> /// </summary>
/// <remarks>This is usually used to report a faulting cpu address when a <see cref="Server.MemoryPool.MemoryPoolState"/> mapping fail.</remarks> /// <remarks>This is usually used to report a faulting cpu address when a <see cref="MemoryPoolInfo"/> mapping fail.</remarks>
public ulong ExtraErrorInfo; public ulong ExtraErrorInfo;
} }
} }

View File

@@ -1,4 +1,5 @@
using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System; using System;
@@ -11,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Common
/// </summary> /// </summary>
/// <remarks>This is shared between the server and audio processor.</remarks> /// <remarks>This is shared between the server and audio processor.</remarks>
[StructLayout(LayoutKind.Sequential, Pack = Align)] [StructLayout(LayoutKind.Sequential, Pack = Align)]
public struct VoiceUpdateState public struct VoiceState
{ {
public const int Align = 0x10; public const int Align = 0x10;
public const int BiquadStateOffset = 0x0; public const int BiquadStateOffset = 0x0;
@@ -25,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Common
/// The total amount of samples that was played. /// The total amount of samples that was played.
/// </summary> /// </summary>
/// <remarks>This is reset to 0 when a <see cref="WaveBuffer"/> finishes playing and <see cref="WaveBuffer.IsEndOfStream"/> is set.</remarks> /// <remarks>This is reset to 0 when a <see cref="WaveBuffer"/> finishes playing and <see cref="WaveBuffer.IsEndOfStream"/> is set.</remarks>
/// <remarks>This is reset to 0 when looping while <see cref="Parameter.VoiceInParameter.DecodingBehaviour.PlayedSampleCountResetWhenLooping"/> is set.</remarks> /// <remarks>This is reset to 0 when looping while <see cref="VoiceInParameter1.DecodingBehaviour.PlayedSampleCountResetWhenLooping"/> is set.</remarks>
public ulong PlayedSampleCount; public ulong PlayedSampleCount;
/// <summary> /// <summary>

View File

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

View File

@@ -1,5 +1,7 @@
using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using Ryujinx.Common.Memory;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -9,6 +11,112 @@ namespace Ryujinx.Audio.Renderer.Dsp
{ {
private const int FixedPointPrecisionForParameter = 14; private const int FixedPointPrecisionForParameter = 14;
public static BiquadFilterParameter1 ToBiquadFilterParameter1(BiquadFilterParameter2 parameter)
{
BiquadFilterParameter1 result = new()
{
Enable = parameter.Enable, Numerator = new Array3<short>(), Denominator = new Array2<short>()
};
Span<short> resultNumeratorSpan = result.Numerator.AsSpan();
Span<short> resultDenominatorSpan = result.Denominator.AsSpan();
Span<float> parameterNumeratorSpan = parameter.Numerator.AsSpan();
Span<float> parameterDenominatorSpan = parameter.Denominator.AsSpan();
resultNumeratorSpan[0] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[0], FixedPointPrecisionForParameter);
resultNumeratorSpan[1] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[1], FixedPointPrecisionForParameter);
resultNumeratorSpan[2] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[2], FixedPointPrecisionForParameter);
resultDenominatorSpan[0] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[0], FixedPointPrecisionForParameter);
resultDenominatorSpan[1] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[1], FixedPointPrecisionForParameter);
return result;
}
public static BiquadFilterParameter2 ToBiquadFilterParameter2(BiquadFilterParameter1 parameter)
{
BiquadFilterParameter2 result = new()
{
Enable = parameter.Enable, Numerator = new Array3<float>(), Denominator = new Array2<float>()
};
Span<float> resultNumeratorSpan = result.Numerator.AsSpan();
Span<float> resultDenominatorSpan = result.Denominator.AsSpan();
Span<short> parameterNumeratorSpan = parameter.Numerator.AsSpan();
Span<short> parameterDenominatorSpan = parameter.Denominator.AsSpan();
resultNumeratorSpan[0] = FixedPointHelper.ToFloat(parameterNumeratorSpan[0], FixedPointPrecisionForParameter);
resultNumeratorSpan[1] = FixedPointHelper.ToFloat(parameterNumeratorSpan[1], FixedPointPrecisionForParameter);
resultNumeratorSpan[2] = FixedPointHelper.ToFloat(parameterNumeratorSpan[2], FixedPointPrecisionForParameter);
resultDenominatorSpan[0] = FixedPointHelper.ToFloat(parameterDenominatorSpan[0], FixedPointPrecisionForParameter);
resultDenominatorSpan[1] = FixedPointHelper.ToFloat(parameterDenominatorSpan[1], FixedPointPrecisionForParameter);
return result;
}
public static BiquadFilterEffectParameter1 ToBiquadFilterEffectParameter1(BiquadFilterEffectParameter2 parameter)
{
BiquadFilterEffectParameter1 result = new()
{
Input = parameter.Input,
Output = parameter.Output,
Numerator = new Array3<short>(),
Denominator = new Array2<short>(),
ChannelCount = parameter.ChannelCount,
Status = parameter.Status,
};
Span<short> resultNumeratorSpan = result.Numerator.AsSpan();
Span<short> resultDenominatorSpan = result.Denominator.AsSpan();
Span<float> parameterNumeratorSpan = parameter.Numerator.AsSpan();
Span<float> parameterDenominatorSpan = parameter.Denominator.AsSpan();
resultNumeratorSpan[0] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[0], FixedPointPrecisionForParameter);
resultNumeratorSpan[1] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[1], FixedPointPrecisionForParameter);
resultNumeratorSpan[2] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[2], FixedPointPrecisionForParameter);
resultDenominatorSpan[0] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[0], FixedPointPrecisionForParameter);
resultDenominatorSpan[1] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[1], FixedPointPrecisionForParameter);
return result;
}
public static BiquadFilterEffectParameter2 ToBiquadFilterEffectParameter2(BiquadFilterEffectParameter1 parameter)
{
BiquadFilterEffectParameter2 result = new()
{
Input = parameter.Input,
Output = parameter.Output,
Numerator = new Array3<float>(),
Denominator = new Array2<float>(),
ChannelCount = parameter.ChannelCount,
Status = parameter.Status,
};
Span<float> resultNumeratorSpan = result.Numerator.AsSpan();
Span<float> resultDenominatorSpan = result.Denominator.AsSpan();
Span<short> parameterNumeratorSpan = parameter.Numerator.AsSpan();
Span<short> parameterDenominatorSpan = parameter.Denominator.AsSpan();
resultNumeratorSpan[0] = FixedPointHelper.ToFloat(parameterNumeratorSpan[0], FixedPointPrecisionForParameter);
resultNumeratorSpan[1] = FixedPointHelper.ToFloat(parameterNumeratorSpan[1], FixedPointPrecisionForParameter);
resultNumeratorSpan[2] = FixedPointHelper.ToFloat(parameterNumeratorSpan[2], FixedPointPrecisionForParameter);
resultDenominatorSpan[0] = FixedPointHelper.ToFloat(parameterDenominatorSpan[0], FixedPointPrecisionForParameter);
resultDenominatorSpan[1] = FixedPointHelper.ToFloat(parameterDenominatorSpan[1], FixedPointPrecisionForParameter);
return result;
}
/// <summary> /// <summary>
/// Apply a single biquad filter. /// Apply a single biquad filter.
/// </summary> /// </summary>
@@ -20,18 +128,21 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="sampleCount">The count of samples to process</param> /// <param name="sampleCount">The count of samples to process</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProcessBiquadFilter( public static void ProcessBiquadFilter(
ref BiquadFilterParameter parameter, ref BiquadFilterParameter2 parameter,
ref BiquadFilterState state, ref BiquadFilterState state,
Span<float> outputBuffer, Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer, ReadOnlySpan<float> inputBuffer,
uint sampleCount) uint sampleCount)
{ {
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); Span<float> numeratorSpan = parameter.Numerator.AsSpan();
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); Span<float> denominatorSpan = parameter.Denominator.AsSpan();
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = numeratorSpan[0];
float a1 = numeratorSpan[1];
float a2 = numeratorSpan[2];
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); float b1 = denominatorSpan[0];
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); float b2 = denominatorSpan[1];
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -57,19 +168,22 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="volume">Mix volume</param> /// <param name="volume">Mix volume</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProcessBiquadFilterAndMix( public static void ProcessBiquadFilterAndMix(
ref BiquadFilterParameter parameter, ref BiquadFilterParameter2 parameter,
ref BiquadFilterState state, ref BiquadFilterState state,
Span<float> outputBuffer, Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer, ReadOnlySpan<float> inputBuffer,
uint sampleCount, uint sampleCount,
float volume) float volume)
{ {
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); Span<float> numeratorSpan = parameter.Numerator.AsSpan();
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); Span<float> denominatorSpan = parameter.Denominator.AsSpan();
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = numeratorSpan[0];
float a1 = numeratorSpan[1];
float a2 = numeratorSpan[2];
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); float b1 = denominatorSpan[0];
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); float b2 = denominatorSpan[1];
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -99,7 +213,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <returns>Last filtered sample value</returns> /// <returns>Last filtered sample value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ProcessBiquadFilterAndMixRamp( public static float ProcessBiquadFilterAndMixRamp(
ref BiquadFilterParameter parameter, ref BiquadFilterParameter2 parameter,
ref BiquadFilterState state, ref BiquadFilterState state,
Span<float> outputBuffer, Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer, ReadOnlySpan<float> inputBuffer,
@@ -107,12 +221,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
float volume, float volume,
float ramp) float ramp)
{ {
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); Span<float> numeratorSpan = parameter.Numerator.AsSpan();
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); Span<float> denominatorSpan = parameter.Denominator.AsSpan();
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
float a0 = numeratorSpan[0];
float a1 = numeratorSpan[1];
float a2 = numeratorSpan[2];
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); float b1 = denominatorSpan[0];
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); float b2 = denominatorSpan[1];
float mixState = 0f; float mixState = 0f;
@@ -146,7 +263,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="sampleCount">The count of samples to process</param> /// <param name="sampleCount">The count of samples to process</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProcessBiquadFilter( public static void ProcessBiquadFilter(
ReadOnlySpan<BiquadFilterParameter> parameters, ReadOnlySpan<BiquadFilterParameter2> parameters,
Span<BiquadFilterState> states, Span<BiquadFilterState> states,
Span<float> outputBuffer, Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer, ReadOnlySpan<float> inputBuffer,
@@ -154,16 +271,19 @@ namespace Ryujinx.Audio.Renderer.Dsp
{ {
for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++) for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++)
{ {
BiquadFilterParameter parameter = parameters[stageIndex]; BiquadFilterParameter2 parameter = parameters[stageIndex];
ref BiquadFilterState state = ref states[stageIndex]; ref BiquadFilterState state = ref states[stageIndex];
Span<float> numeratorSpan = parameter.Numerator.AsSpan();
Span<float> denominatorSpan = parameter.Denominator.AsSpan();
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); float a0 = numeratorSpan[0];
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); float a1 = numeratorSpan[1];
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); float a2 = numeratorSpan[2];
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); float b1 = denominatorSpan[0];
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); float b2 = denominatorSpan[1];
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -192,8 +312,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="volume">Mix volume</param> /// <param name="volume">Mix volume</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProcessDoubleBiquadFilterAndMix( public static void ProcessDoubleBiquadFilterAndMix(
ref BiquadFilterParameter parameter0, ref BiquadFilterParameter2 parameter0,
ref BiquadFilterParameter parameter1, ref BiquadFilterParameter2 parameter1,
ref BiquadFilterState state0, ref BiquadFilterState state0,
ref BiquadFilterState state1, ref BiquadFilterState state1,
Span<float> outputBuffer, Span<float> outputBuffer,
@@ -201,19 +321,25 @@ namespace Ryujinx.Audio.Renderer.Dsp
uint sampleCount, uint sampleCount,
float volume) float volume)
{ {
float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter); Span<float> numerator0Span = parameter0.Numerator.AsSpan();
float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter); Span<float> numerator1Span = parameter1.Numerator.AsSpan();
float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter); Span<float> denominator0Span = parameter0.Denominator.AsSpan();
Span<float> denominator1Span = parameter1.Denominator.AsSpan();
float a00 = numerator0Span[0];
float a10 = numerator0Span[1];
float a20 = numerator0Span[2];
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter); float b10 = denominator0Span[0];
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter); float b20 = denominator0Span[1];
float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter); float a01 = numerator1Span[0];
float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter); float a11 = numerator1Span[1];
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter); float a21 = numerator1Span[2];
float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter); float b11 = denominator1Span[0];
float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter); float b21 = denominator1Span[1];
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
@@ -251,8 +377,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <returns>Last filtered sample value</returns> /// <returns>Last filtered sample value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ProcessDoubleBiquadFilterAndMixRamp( public static float ProcessDoubleBiquadFilterAndMixRamp(
ref BiquadFilterParameter parameter0, ref BiquadFilterParameter2 parameter0,
ref BiquadFilterParameter parameter1, ref BiquadFilterParameter2 parameter1,
ref BiquadFilterState state0, ref BiquadFilterState state0,
ref BiquadFilterState state1, ref BiquadFilterState state1,
Span<float> outputBuffer, Span<float> outputBuffer,
@@ -261,19 +387,24 @@ namespace Ryujinx.Audio.Renderer.Dsp
float volume, float volume,
float ramp) float ramp)
{ {
float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter); Span<float> numerator0Span = parameter0.Numerator.AsSpan();
float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter); Span<float> numerator1Span = parameter1.Numerator.AsSpan();
float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter); Span<float> denominator0Span = parameter0.Denominator.AsSpan();
Span<float> denominator1Span = parameter1.Denominator.AsSpan();
float a00 = numerator0Span[0];
float a10 = numerator0Span[1];
float a20 = numerator0Span[2];
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter); float b10 = denominator0Span[0];
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter); float b20 = denominator0Span[1];
float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter); float a01 = numerator1Span[0];
float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter); float a11 = numerator1Span[1];
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter); float a21 = numerator1Span[2];
float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter); float b11 = denominator1Span[0];
float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter); float b21 = denominator1Span[1];
float mixState = 0f; float mixState = 0f;

View File

@@ -2,7 +2,7 @@ using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Server.Voice;
using System; using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer;
namespace Ryujinx.Audio.Renderer.Dsp.Command namespace Ryujinx.Audio.Renderer.Dsp.Command
@@ -24,35 +24,37 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public WaveBuffer[] WaveBuffers { get; } public WaveBuffer[] WaveBuffers { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public ulong AdpcmParameter { get; } public ulong AdpcmParameter { get; }
public ulong AdpcmParameterSize { get; } public ulong AdpcmParameterSize { get; }
public DecodingBehaviour DecodingBehaviour { get; } public DecodingBehaviour DecodingBehaviour { get; }
public AdpcmDataSourceCommandVersion1(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, int nodeId) public AdpcmDataSourceCommandVersion1(ref VoiceInfo serverInfo, Memory<VoiceState> state, ushort outputBufferIndex, int nodeId)
{ {
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;
OutputBufferIndex = outputBufferIndex; OutputBufferIndex = outputBufferIndex;
SampleRate = serverState.SampleRate; SampleRate = serverInfo.SampleRate;
Pitch = serverState.Pitch; Pitch = serverInfo.Pitch;
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverInfo.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);
} }
AdpcmParameter = serverState.DataSourceStateAddressInfo.GetReference(true); AdpcmParameter = serverInfo.DataSourceStateAddressInfo.GetReference(true);
AdpcmParameterSize = serverState.DataSourceStateAddressInfo.Size; AdpcmParameterSize = serverInfo.DataSourceStateAddressInfo.Size;
State = state; State = state;
DecodingBehaviour = serverState.DecodingBehaviour; DecodingBehaviour = serverInfo.DecodingBehaviour;
} }
public void Process(CommandList context) public void Process(CommandList context)

View File

@@ -18,12 +18,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public ushort InputBufferIndex { get; } public ushort InputBufferIndex { get; }
public ushort OutputBufferIndex { get; } public ushort OutputBufferIndex { get; }
private BiquadFilterParameter _parameter; private BiquadFilterParameter2 _parameter;
public Memory<BiquadFilterState> BiquadFilterState { get; } public Memory<BiquadFilterState> BiquadFilterState { get; }
public Memory<BiquadFilterState> PreviousBiquadFilterState { get; } public Memory<BiquadFilterState> PreviousBiquadFilterState { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public int LastSampleIndex { get; } public int LastSampleIndex { get; }
@@ -40,8 +40,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
uint inputBufferIndex, uint inputBufferIndex,
uint outputBufferIndex, uint outputBufferIndex,
int lastSampleIndex, int lastSampleIndex,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
ref BiquadFilterParameter filter, ref BiquadFilterParameter2 filter,
Memory<BiquadFilterState> biquadFilterState, Memory<BiquadFilterState> biquadFilterState,
Memory<BiquadFilterState> previousBiquadFilterState, Memory<BiquadFilterState> previousBiquadFilterState,
bool needInitialization, bool needInitialization,

View File

@@ -19,11 +19,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public int OutputBufferIndex { get; } public int OutputBufferIndex { get; }
public bool NeedInitialization { get; } public bool NeedInitialization { get; }
private BiquadFilterParameter _parameter; private BiquadFilterParameter2 _parameter;
public BiquadFilterCommand( public BiquadFilterCommand(
int baseIndex, int baseIndex,
ref BiquadFilterParameter filter, ref BiquadFilterParameter2 filter,
Memory<BiquadFilterState> biquadFilterStateMemory, Memory<BiquadFilterState> biquadFilterStateMemory,
int inputBufferOffset, int inputBufferOffset,
int outputBufferOffset, int outputBufferOffset,

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

@@ -129,7 +129,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
{ {
startTime = PerformanceCounter.ElapsedNanoseconds; startTime = PerformanceCounter.ElapsedNanoseconds;
} }
command.Process(this); command.Process(this);
if (shouldMeter) if (shouldMeter)

View File

@@ -12,6 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Volume, Volume,
VolumeRamp, VolumeRamp,
BiquadFilter, BiquadFilter,
BiquadFilterFloatCoeff, // new
Mix, Mix,
MixRamp, MixRamp,
MixRampGrouped, MixRampGrouped,
@@ -31,9 +32,17 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
LimiterVersion1, LimiterVersion1,
LimiterVersion2, LimiterVersion2,
MultiTapBiquadFilter, MultiTapBiquadFilter,
MultiTapBiquadFilterFloatCoeff, // new
CaptureBuffer, CaptureBuffer,
Compressor, Compressor,
BiquadFilterAndMix, BiquadFilterAndMix,
BiquadFilterAndMixFloatCoeff, // new
MultiTapBiquadFilterAndMix, MultiTapBiquadFilterAndMix,
MultiTapBiquadFilterAndMixFloatCoef, // new
AuxiliaryBufferGrouped, // new
FillMixBuffer, // new
BiquadFilterCrossFade, // new
MultiTapBiquadFilterCrossFade, // new
FillBuffer, // new
} }
} }

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

@@ -2,7 +2,7 @@ using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Server.Voice;
using System; using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer;
namespace Ryujinx.Audio.Renderer.Dsp.Command namespace Ryujinx.Audio.Renderer.Dsp.Command
@@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public WaveBuffer[] WaveBuffers { get; } public WaveBuffer[] WaveBuffers { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public ulong ExtraParameter { get; } public ulong ExtraParameter { get; }
public ulong ExtraParameterSize { get; } public ulong ExtraParameterSize { get; }
@@ -39,37 +39,39 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public SampleRateConversionQuality SrcQuality { get; } public SampleRateConversionQuality SrcQuality { get; }
public DataSourceVersion2Command(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) public DataSourceVersion2Command(ref VoiceInfo serverInfo, Memory<VoiceState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId)
{ {
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;
ChannelIndex = channelIndex; ChannelIndex = channelIndex;
ChannelCount = serverState.ChannelsCount; ChannelCount = serverInfo.ChannelsCount;
SampleFormat = serverState.SampleFormat; SampleFormat = serverInfo.SampleFormat;
SrcQuality = serverState.SrcQuality; SrcQuality = serverInfo.SrcQuality;
CommandType = GetCommandTypeBySampleFormat(SampleFormat); CommandType = GetCommandTypeBySampleFormat(SampleFormat);
OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex);
SampleRate = serverState.SampleRate; SampleRate = serverInfo.SampleRate;
Pitch = serverState.Pitch; Pitch = serverInfo.Pitch;
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverInfo.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);
} }
if (SampleFormat == SampleFormat.Adpcm) if (SampleFormat == SampleFormat.Adpcm)
{ {
ExtraParameter = serverState.DataSourceStateAddressInfo.GetReference(true); ExtraParameter = serverInfo.DataSourceStateAddressInfo.GetReference(true);
ExtraParameterSize = serverState.DataSourceStateAddressInfo.Size; ExtraParameterSize = serverInfo.DataSourceStateAddressInfo.Size;
} }
State = state; State = state;
DecodingBehaviour = serverState.DecodingBehaviour; DecodingBehaviour = serverInfo.DecodingBehaviour;
} }
private static CommandType GetCommandTypeBySampleFormat(SampleFormat sampleFormat) private static CommandType GetCommandTypeBySampleFormat(SampleFormat sampleFormat)

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

@@ -17,10 +17,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public ushort[] OutputBufferIndices { get; } public ushort[] OutputBufferIndices { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public Memory<float> DepopBuffer { get; } public Memory<float> DepopBuffer { get; }
public DepopPrepareCommand(Memory<VoiceUpdateState> state, Memory<float> depopBuffer, uint mixBufferCount, uint bufferOffset, int nodeId, bool enabled) public DepopPrepareCommand(Memory<VoiceState> state, Memory<float> depopBuffer, uint mixBufferCount, uint bufferOffset, int nodeId, bool enabled)
{ {
Enabled = enabled; Enabled = enabled;
NodeId = nodeId; NodeId = nodeId;
@@ -39,17 +39,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public void Process(CommandList context) public void Process(CommandList context)
{ {
ref VoiceUpdateState state = ref State.Span[0]; ref VoiceState 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,15 +34,17 @@ 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.UpsamplerInfo != null)
{ {
Buffers = sink.UpsamplerState.OutputBuffer; Buffers = sink.UpsamplerInfo.OutputBuffer;
} }
else else
{ {

View File

@@ -0,0 +1,69 @@
using Ryujinx.Audio.Renderer.Server.Splitter;
using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp.Command
{
public class FillBufferCommand : ICommand
{
public bool Enabled { get; set; }
public int NodeId { get; }
public CommandType CommandType => CommandType.FillBuffer;
public uint EstimatedProcessingTime { get; set; }
public SplitterDestinationVersion1 Destination1 { get; }
public SplitterDestinationVersion2 Destination2 { get; }
public bool IsV2 { get; }
public int Length { get; }
public float Value { get; }
public FillBufferCommand(SplitterDestinationVersion1 destination, int length, float value, int nodeId)
{
Enabled = true;
NodeId = nodeId;
Destination1 = destination;
IsV2 = false;
Length = length;
Value = value;
}
public FillBufferCommand(SplitterDestinationVersion2 destination, int length, float value, int nodeId)
{
Enabled = true;
NodeId = nodeId;
Destination2 = destination;
IsV2 = true;
Length = length;
Value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessFillBuffer()
{
if (IsV2)
{
for (int i = 0; i < Length; i++)
{
Destination2.PreviousMixBufferVolume[i] = Value;
}
}
else
{
for (int i = 0; i < Length; i++)
{
Destination1.PreviousMixBufferVolume[i] = Value;
}
}
}
public void Process(CommandList context)
{
ProcessFillBuffer();
}
}
}

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

@@ -20,11 +20,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public float Volume0 { get; } public float Volume0 { get; }
public float Volume1 { get; } public float Volume1 { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public int LastSampleIndex { get; } public int LastSampleIndex { get; }
public MixRampCommand(float volume0, float volume1, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory<VoiceUpdateState> state, int nodeId) public MixRampCommand(float volume0, float volume1, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory<VoiceState> state, int nodeId)
{ {
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public float[] Volume0 { get; } public float[] Volume0 { get; }
public float[] Volume1 { get; } public float[] Volume1 { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public MixRampGroupedCommand( public MixRampGroupedCommand(
uint mixBufferCount, uint mixBufferCount,
@@ -30,7 +30,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
uint outputBufferIndex, uint outputBufferIndex,
ReadOnlySpan<float> volume0, ReadOnlySpan<float> volume0,
ReadOnlySpan<float> volume1, ReadOnlySpan<float> volume1,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
int nodeId) int nodeId)
{ {
Enabled = true; Enabled = true;
@@ -79,6 +79,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public void Process(CommandList context) public void Process(CommandList context)
{ {
ref VoiceState 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

@@ -18,15 +18,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public ushort InputBufferIndex { get; } public ushort InputBufferIndex { get; }
public ushort OutputBufferIndex { get; } public ushort OutputBufferIndex { get; }
private BiquadFilterParameter _parameter0; private BiquadFilterParameter2 _parameter0;
private BiquadFilterParameter _parameter1; private BiquadFilterParameter2 _parameter1;
public Memory<BiquadFilterState> BiquadFilterState0 { get; } public Memory<BiquadFilterState> BiquadFilterState0 { get; }
public Memory<BiquadFilterState> BiquadFilterState1 { get; } public Memory<BiquadFilterState> BiquadFilterState1 { get; }
public Memory<BiquadFilterState> PreviousBiquadFilterState0 { get; } public Memory<BiquadFilterState> PreviousBiquadFilterState0 { get; }
public Memory<BiquadFilterState> PreviousBiquadFilterState1 { get; } public Memory<BiquadFilterState> PreviousBiquadFilterState1 { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public int LastSampleIndex { get; } public int LastSampleIndex { get; }
@@ -44,9 +44,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
uint inputBufferIndex, uint inputBufferIndex,
uint outputBufferIndex, uint outputBufferIndex,
int lastSampleIndex, int lastSampleIndex,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
ref BiquadFilterParameter filter0, ref BiquadFilterParameter2 filter0,
ref BiquadFilterParameter filter1, ref BiquadFilterParameter2 filter1,
Memory<BiquadFilterState> biquadFilterState0, Memory<BiquadFilterState> biquadFilterState0,
Memory<BiquadFilterState> biquadFilterState1, Memory<BiquadFilterState> biquadFilterState1,
Memory<BiquadFilterState> previousBiquadFilterState0, Memory<BiquadFilterState> previousBiquadFilterState0,

View File

@@ -14,13 +14,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public uint EstimatedProcessingTime { get; set; } public uint EstimatedProcessingTime { get; set; }
private readonly BiquadFilterParameter[] _parameters; private readonly BiquadFilterParameter2[] _parameters;
private readonly Memory<BiquadFilterState> _biquadFilterStates; private readonly Memory<BiquadFilterState> _biquadFilterStates;
private readonly int _inputBufferIndex; private readonly int _inputBufferIndex;
private readonly int _outputBufferIndex; private readonly int _outputBufferIndex;
private readonly bool[] _isInitialized; private readonly bool[] _isInitialized;
public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId) public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter2> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
{ {
_parameters = filters.ToArray(); _parameters = filters.ToArray();
_biquadFilterStates = biquadFilterStateMemory; _biquadFilterStates = biquadFilterStateMemory;

View File

@@ -2,7 +2,7 @@ using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Server.Voice;
using System; using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer;
namespace Ryujinx.Audio.Renderer.Dsp.Command namespace Ryujinx.Audio.Renderer.Dsp.Command
@@ -27,31 +27,33 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public WaveBuffer[] WaveBuffers { get; } public WaveBuffer[] WaveBuffers { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public DecodingBehaviour DecodingBehaviour { get; } public DecodingBehaviour DecodingBehaviour { get; }
public PcmFloatDataSourceCommandVersion1(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) public PcmFloatDataSourceCommandVersion1(ref VoiceInfo serverInfo, Memory<VoiceState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId)
{ {
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;
OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex);
SampleRate = serverState.SampleRate; SampleRate = serverInfo.SampleRate;
ChannelIndex = channelIndex; ChannelIndex = channelIndex;
ChannelCount = serverState.ChannelsCount; ChannelCount = serverInfo.ChannelsCount;
Pitch = serverState.Pitch; Pitch = serverInfo.Pitch;
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverInfo.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);
} }
State = state; State = state;
DecodingBehaviour = serverState.DecodingBehaviour; DecodingBehaviour = serverInfo.DecodingBehaviour;
} }
public void Process(CommandList context) public void Process(CommandList context)

View File

@@ -2,7 +2,7 @@ using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Server.Voice;
using System; using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer;
namespace Ryujinx.Audio.Renderer.Dsp.Command namespace Ryujinx.Audio.Renderer.Dsp.Command
@@ -27,31 +27,33 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public WaveBuffer[] WaveBuffers { get; } public WaveBuffer[] WaveBuffers { get; }
public Memory<VoiceUpdateState> State { get; } public Memory<VoiceState> State { get; }
public DecodingBehaviour DecodingBehaviour { get; } public DecodingBehaviour DecodingBehaviour { get; }
public PcmInt16DataSourceCommandVersion1(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) public PcmInt16DataSourceCommandVersion1(ref VoiceInfo serverInfo, Memory<VoiceState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId)
{ {
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;
OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex);
SampleRate = serverState.SampleRate; SampleRate = serverInfo.SampleRate;
ChannelIndex = channelIndex; ChannelIndex = channelIndex;
ChannelCount = serverState.ChannelsCount; ChannelCount = serverInfo.ChannelsCount;
Pitch = serverState.Pitch; Pitch = serverInfo.Pitch;
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverInfo.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);
} }
State = state; State = state;
DecodingBehaviour = serverState.DecodingBehaviour; DecodingBehaviour = serverInfo.DecodingBehaviour;
} }
public void Process(CommandList context) public void Process(CommandList context)

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

@@ -18,11 +18,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public uint InputSampleCount { get; } public uint InputSampleCount { get; }
public uint InputSampleRate { get; } public uint InputSampleRate { get; }
public UpsamplerState UpsamplerInfo { get; } public UpsamplerInfo UpsamplerInfo { get; }
public Memory<float> OutBuffer { get; } public Memory<float> OutBuffer { get; }
public UpsampleCommand(uint bufferOffset, UpsamplerState info, uint inputCount, Span<byte> inputBufferOffset, uint bufferCount, uint sampleCount, uint sampleRate, int nodeId) public UpsampleCommand(uint bufferOffset, UpsamplerInfo info, uint inputCount, Span<byte> inputBufferOffset, uint bufferCount, uint sampleCount, uint sampleRate, int nodeId)
{ {
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;

View File

@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
namespace Ryujinx.Audio.Renderer.Dsp namespace Ryujinx.Audio.Renderer.Dsp
{ {
@@ -42,7 +42,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
}; };
} }
public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span<float> outputBuffer, ref WaveBufferInformation info, Span<WaveBuffer> wavebuffers, ref VoiceUpdateState voiceState, uint targetSampleRate, int sampleCount) public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span<float> outputBuffer, ref WaveBufferInformation info, Span<WaveBuffer> wavebuffers, ref VoiceState voiceState, uint targetSampleRate, int sampleCount)
{ {
const int TempBufferSize = 0x3F00; const int TempBufferSize = 0x3F00;
@@ -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

@@ -3,7 +3,7 @@ using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
namespace Ryujinx.Audio.Renderer.Dsp namespace Ryujinx.Audio.Renderer.Dsp
{ {

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

@@ -1,3 +1,4 @@
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.Audio.Renderer.Server.Types; using Ryujinx.Audio.Renderer.Server.Types;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -93,7 +94,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <summary> /// <summary>
/// The user audio revision /// The user audio revision
/// </summary> /// </summary>
/// <seealso cref="Server.BehaviourContext"/> /// <seealso cref="BehaviourInfo"/>
public int Revision; public int Revision;
} }
} }

View File

@@ -7,7 +7,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// Biquad filter parameters. /// Biquad filter parameters.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0xC, Pack = 1)] [StructLayout(LayoutKind.Sequential, Size = 0xC, Pack = 1)]
public struct BiquadFilterParameter public struct BiquadFilterParameter1
{ {
/// <summary> /// <summary>
/// Set to true if the biquad filter is active. /// Set to true if the biquad filter is active.

View File

@@ -0,0 +1,36 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
/// Biquad filter parameters.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x18, Pack = 1)]
public struct BiquadFilterParameter2
{
/// <summary>
/// Set to true if the biquad filter is active.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool Enable;
/// <summary>
/// Reserved/padding.
/// </summary>
private readonly byte _reserved1;
private readonly byte _reserved2;
private readonly byte _reserved3;
/// <summary>
/// Biquad filter numerator (b0, b1, b2).
/// </summary>
public Array3<float> Numerator;
/// <summary>
/// Biquad filter denominator (a1, a2).
/// </summary>
/// <remarks>a0 = 1</remarks>
public Array2<float> Denominator;
}
}

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>. /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BiquadFilterEffectParameter public struct BiquadFilterEffectParameter1
{ {
/// <summary> /// <summary>
/// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>. /// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.

View File

@@ -0,0 +1,49 @@
using Ryujinx.Audio.Renderer.Server.Effect;
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BiquadFilterEffectParameter2
{
/// <summary>
/// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
/// </summary>
public Array6<byte> Input;
/// <summary>
/// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
/// </summary>
public Array6<byte> Output;
/// <summary>
/// Biquad filter numerator (b0, b1, b2).
/// </summary>
public Array3<float> Numerator;
/// <summary>
/// Biquad filter denominator (a1, a2).
/// </summary>
/// <remarks>a0 = 1</remarks>
public Array2<float> Denominator;
/// <summary>
/// The total channel count used.
/// </summary>
public byte ChannelCount;
/// <summary>
/// The current usage status of the effect on the client side.
/// </summary>
public UsageState Status;
/// <summary>
/// Reserved/unused.
/// </summary>
private readonly ushort _reserved;
}
}

View File

@@ -0,0 +1,97 @@
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Common.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
/// Input information for an effect version 2. (added with REV9)
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct EffectInParameterVersion3 : IEffectInParameter
{
/// <summary>
/// Type of the effect.
/// </summary>
public EffectType Type;
/// <summary>
/// Set to true if the effect is new.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool IsNew;
/// <summary>
/// Set to true if the effect must be active.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool IsEnabled;
/// <summary>
/// Reserved/padding.
/// </summary>
private readonly byte _reserved1;
/// <summary>
/// The target mix id of the effect.
/// </summary>
public int MixId;
/// <summary>
/// Address of the processing workbuffer.
/// </summary>
/// <remarks>This is additional data that could be required by the effect processing.</remarks>
public ulong BufferBase;
/// <summary>
/// Size of the processing workbuffer.
/// </summary>
/// <remarks>This is additional data that could be required by the effect processing.</remarks>
public ulong BufferSize;
/// <summary>
/// Position of the effect while processing effects.
/// </summary>
public uint ProcessingOrder;
/// <summary>
/// Reserved/padding.
/// </summary>
private readonly uint _reserved2;
/// <summary>
/// Specific data storage.
/// </summary>
private SpecificDataStruct _specificDataStart;
[StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 1)]
private struct SpecificDataStruct { }
public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
readonly EffectType IEffectInParameter.Type => Type;
readonly bool IEffectInParameter.IsNew => IsNew;
readonly bool IEffectInParameter.IsEnabled => IsEnabled;
readonly int IEffectInParameter.MixId => MixId;
readonly ulong IEffectInParameter.BufferBase => BufferBase;
readonly ulong IEffectInParameter.BufferSize => BufferSize;
readonly uint IEffectInParameter.ProcessingOrder => ProcessingOrder;
/// <summary>
/// Check if the given channel count is valid.
/// </summary>
/// <param name="channelCount">The channel count to check</param>
/// <returns>Returns true if the channel count is valid.</returns>
public static bool IsChannelCountValid(int channelCount)
{
return channelCount is 1 or 2 or 4 or 6;
}
}
}

View File

@@ -17,11 +17,11 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// The mix to output the result of the splitter. /// The mix to output the result of the splitter.
/// </summary> /// </summary>
int DestinationId { get; } int DestinationId { get; }
/// <summary> /// <summary>
/// Biquad filter parameters. /// Biquad filter parameters.
/// </summary> /// </summary>
Array2<BiquadFilterParameter> BiquadFilters { get; } Array2<BiquadFilterParameter2> BiquadFilters2 { get; }
/// <summary> /// <summary>
/// Set to true if in use. /// Set to true if in use.

View File

@@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <summary> /// <summary>
/// Reserved/padding. /// Reserved/padding.
/// </summary> /// </summary>
private readonly ushort _reserved1; private readonly ushort _magic; // 0xCAFE
/// <summary> /// <summary>
/// The node id of the sink. /// The node id of the sink.

View File

@@ -60,8 +60,8 @@ namespace Ryujinx.Audio.Renderer.Parameter
readonly int ISplitterDestinationInParameter.Id => Id; readonly int ISplitterDestinationInParameter.Id => Id;
readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; readonly int ISplitterDestinationInParameter.DestinationId => DestinationId;
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default; readonly Array2<BiquadFilterParameter2> ISplitterDestinationInParameter.BiquadFilters2 => default;
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;

View File

@@ -0,0 +1,100 @@
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
/// Input header for a splitter destination version 2 update.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SplitterDestinationInParameterVersion2a : ISplitterDestinationInParameter
{
/// <summary>
/// Magic of the input header.
/// </summary>
public uint Magic;
/// <summary>
/// Target splitter destination data id.
/// </summary>
public int Id;
/// <summary>
/// Mix buffer volumes storage.
/// </summary>
private MixArray _mixBufferVolume;
/// <summary>
/// The mix to output the result of the splitter.
/// </summary>
public int DestinationId;
/// <summary>
/// Biquad filter parameters.
/// </summary>
public Array2<BiquadFilterParameter1> BiquadFilters;
/// <summary>
/// Set to true if in use.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool IsUsed;
/// <summary>
/// Set to true to force resetting the previous mix volumes.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool ResetPrevVolume;
/// <summary>
/// Reserved/padding.
/// </summary>
private unsafe fixed byte _reserved[10];
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
private struct MixArray { }
/// <summary>
/// Mix buffer volumes.
/// </summary>
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mixBufferVolume);
readonly int ISplitterDestinationInParameter.Id => Id;
readonly int ISplitterDestinationInParameter.DestinationId => DestinationId;
readonly Array2<BiquadFilterParameter2> ISplitterDestinationInParameter.BiquadFilters2
{
get
{
Array2<BiquadFilterParameter2> newFilters = new();
Span<BiquadFilterParameter2> newFiltersSpan = newFilters.AsSpan();
newFiltersSpan[0] = BiquadFilterHelper.ToBiquadFilterParameter2(BiquadFilters[0]);
newFiltersSpan[1] = BiquadFilterHelper.ToBiquadFilterParameter2(BiquadFilters[1]);
return newFilters;
}
}
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
/// <summary>
/// The expected constant of any input header.
/// </summary>
private const uint ValidMagic = 0x44444E53;
/// <summary>
/// Check if the magic is valid.
/// </summary>
/// <returns>Returns true if the magic is valid.</returns>
public readonly bool IsMagicValid()
{
return Magic == ValidMagic;
}
}
}

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// Input header for a splitter destination version 2 update. /// Input header for a splitter destination version 2 update.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SplitterDestinationInParameterVersion2 : ISplitterDestinationInParameter public struct SplitterDestinationInParameterVersion2b : ISplitterDestinationInParameter
{ {
/// <summary> /// <summary>
/// Magic of the input header. /// Magic of the input header.
@@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <summary> /// <summary>
/// Biquad filter parameters. /// Biquad filter parameters.
/// </summary> /// </summary>
public Array2<BiquadFilterParameter> BiquadFilters; public Array2<BiquadFilterParameter2> BiquadFilters;
/// <summary> /// <summary>
/// Set to true if in use. /// Set to true if in use.
@@ -66,7 +66,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; readonly int ISplitterDestinationInParameter.DestinationId => DestinationId;
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters; readonly Array2<BiquadFilterParameter2> ISplitterDestinationInParameter.BiquadFilters2 => BiquadFilters;
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;

View File

@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// Input information for a voice. /// Input information for a voice.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)] [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)]
public struct VoiceInParameter public struct VoiceInParameter1
{ {
/// <summary> /// <summary>
/// Id of the voice. /// Id of the voice.
@@ -79,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <summary> /// <summary>
/// Biquad filters to apply to the output of the voice. /// Biquad filters to apply to the output of the voice.
/// </summary> /// </summary>
public Array2<BiquadFilterParameter> BiquadFilters; public Array2<BiquadFilterParameter1> BiquadFilters;
/// <summary> /// <summary>
/// Total count of <see cref="WaveBufferInternal"/> of the voice. /// Total count of <see cref="WaveBufferInternal"/> of the voice.
@@ -171,8 +171,9 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// Reserved/unused. /// Reserved/unused.
/// </summary> /// </summary>
private unsafe fixed uint _reserved3[2]; private unsafe fixed uint _reserved3[2];
}
/// <summary>
/// <summary>
/// Input information for a voice wavebuffer. /// Input information for a voice wavebuffer.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)] [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)]
@@ -328,5 +329,4 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// </summary> /// </summary>
Low, Low,
} }
}
} }

View File

@@ -0,0 +1,176 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Common.Memory;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
/// Input information for a voice.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x188, Pack = 1)]
public struct VoiceInParameter2
{
/// <summary>
/// Id of the voice.
/// </summary>
public int Id;
/// <summary>
/// Node id of the voice.
/// </summary>
public int NodeId;
/// <summary>
/// Set to true if the voice is new.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool IsNew;
/// <summary>
/// Set to true if the voice is used.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool InUse;
/// <summary>
/// The voice <see cref="PlayState"/> wanted by the user.
/// </summary>
public PlayState PlayState;
/// <summary>
/// The <see cref="SampleFormat"/> of the voice.
/// </summary>
public SampleFormat SampleFormat;
/// <summary>
/// The sample rate of the voice.
/// </summary>
public uint SampleRate;
/// <summary>
/// The priority of the voice.
/// </summary>
public uint Priority;
/// <summary>
/// Target sorting position of the voice. (Used to sort voices with the same <see cref="Priority"/>)
/// </summary>
public uint SortingOrder;
/// <summary>
/// The total channel count used.
/// </summary>
public uint ChannelCount;
/// <summary>
/// The pitch used on the voice.
/// </summary>
public float Pitch;
/// <summary>
/// The output volume of the voice.
/// </summary>
public float Volume;
/// <summary>
/// Biquad filters to apply to the output of the voice.
/// </summary>
public Array2<BiquadFilterParameter2> BiquadFilters;
/// <summary>
/// Total count of <see cref="WaveBufferInternal"/> of the voice.
/// </summary>
public uint WaveBuffersCount;
/// <summary>
/// Current playing <see cref="WaveBufferInternal"/> of the voice.
/// </summary>
public uint WaveBuffersIndex;
/// <summary>
/// Reserved/unused.
/// </summary>
private readonly uint
_reserved1;
/// <summary>
/// User state address required by the data source.
/// </summary>
/// <remarks>Only used for <see cref="SampleFormat.Adpcm"/> as the address of the GC-ADPCM coefficients.</remarks>
public ulong DataSourceStateAddress;
/// <summary>
/// User state size required by the data source.
/// </summary>
/// <remarks>Only used for <see cref="SampleFormat.Adpcm"/> as the size of the GC-ADPCM coefficients.</remarks>
public ulong DataSourceStateSize;
/// <summary>
/// The target mix id of the voice.
/// </summary>
public int MixId;
/// <summary>
/// The target splitter id of the voice.
/// </summary>
public uint SplitterId;
/// <summary>
/// The wavebuffer parameters of this voice.
/// </summary>
public Array4<WaveBufferInternal> WaveBuffers;
/// <summary>
/// The channel resource ids associated to the voice.
/// </summary>
public Array6<int> ChannelResourceIds;
/// <summary>
/// Reset the voice drop flag during voice server update.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool ResetVoiceDropFlag;
/// <summary>
/// Flush the amount of wavebuffer specified. This will result in the wavebuffer being skipped and marked played.
/// </summary>
/// <remarks>This was added on REV5.</remarks>
public byte FlushWaveBufferCount;
/// <summary>
/// Reserved/unused.
/// </summary>
private readonly ushort _reserved2;
/// <summary>
/// Change the behaviour of the voice.
/// </summary>
/// <remarks>This was added on REV5.</remarks>
public DecodingBehaviour DecodingBehaviourFlags;
/// <summary>
/// Change the Sample Rate Conversion (SRC) quality of the voice.
/// </summary>
/// <remarks>This was added on REV8.</remarks>
public SampleRateConversionQuality SrcQuality;
/// <summary>
/// This was previously used for opus codec support on the Audio Renderer and was removed on REV3.
/// </summary>
public uint ExternalContext;
/// <summary>
/// This was previously used for opus codec support on the Audio Renderer and was removed on REV3.
/// </summary>
public uint ExternalContextSize;
/// <summary>
/// Reserved/unused.
/// </summary>
private unsafe fixed uint _reserved3[2];
}
}

View File

@@ -1,3 +1,5 @@
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Server;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter namespace Ryujinx.Audio.Renderer.Parameter
@@ -5,7 +7,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <summary> /// <summary>
/// Output information about a voice. /// Output information about a voice.
/// </summary> /// </summary>
/// <remarks>See <seealso cref="Server.StateUpdater.UpdateVoices(Server.Voice.VoiceContext, System.Memory{Server.MemoryPool.MemoryPoolState})"/></remarks> /// <remarks>See <seealso cref="StateUpdater.UpdateVoices1"/></remarks>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct VoiceOutStatus public struct VoiceOutStatus
{ {
@@ -13,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// The total amount of samples that was played. /// The total amount of samples that was played.
/// </summary> /// </summary>
/// <remarks>This is reset to 0 when a <see cref="Common.WaveBuffer"/> finishes playing and <see cref="Common.WaveBuffer.IsEndOfStream"/> is set.</remarks> /// <remarks>This is reset to 0 when a <see cref="Common.WaveBuffer"/> finishes playing and <see cref="Common.WaveBuffer.IsEndOfStream"/> is set.</remarks>
/// <remarks>This is reset to 0 when looping while <see cref="Parameter.VoiceInParameter.DecodingBehaviour.PlayedSampleCountResetWhenLooping"/> is set.</remarks> /// <remarks>This is reset to 0 when looping while <see cref="DecodingBehaviour.PlayedSampleCountResetWhenLooping"/> is set.</remarks>
public ulong PlayedSampleCount; public ulong PlayedSampleCount;
/// <summary> /// <summary>

View File

@@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Server
private AudioRendererRenderingDevice _renderingDevice; private AudioRendererRenderingDevice _renderingDevice;
private AudioRendererExecutionMode _executionMode; private AudioRendererExecutionMode _executionMode;
private readonly IWritableEvent _systemEvent; private readonly IWritableEvent _systemEvent;
private MemoryPoolState _dspMemoryPoolState; private MemoryPoolInfo _dspMemoryPoolInfo;
private readonly VoiceContext _voiceContext; private readonly VoiceContext _voiceContext;
private readonly MixContext _mixContext; private readonly MixContext _mixContext;
private readonly SinkContext _sinkContext; private readonly SinkContext _sinkContext;
@@ -40,13 +40,13 @@ namespace Ryujinx.Audio.Renderer.Server
private PerformanceManager _performanceManager; private PerformanceManager _performanceManager;
private UpsamplerManager _upsamplerManager; private UpsamplerManager _upsamplerManager;
private bool _isActive; private bool _isActive;
private BehaviourContext _behaviourContext; private BehaviourInfo _behaviourInfo;
#pragma warning disable IDE0052 // Remove unread private member #pragma warning disable IDE0052 // Remove unread private member
private ulong _totalElapsedTicksUpdating; private ulong _totalElapsedTicksUpdating;
private ulong _totalElapsedTicks; private ulong _totalElapsedTicks;
#pragma warning restore IDE0052 #pragma warning restore IDE0052
private int _sessionId; private int _sessionId;
private Memory<MemoryPoolState> _memoryPools; private Memory<MemoryPoolInfo> _memoryPools;
private uint _sampleRate; private uint _sampleRate;
private uint _sampleCount; private uint _sampleCount;
@@ -84,7 +84,7 @@ namespace Ryujinx.Audio.Renderer.Server
public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent)
{ {
_manager = manager; _manager = manager;
_dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); _dspMemoryPoolInfo = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Dsp);
_voiceContext = new VoiceContext(); _voiceContext = new VoiceContext();
_mixContext = new MixContext(); _mixContext = new MixContext();
_sinkContext = new SinkContext(); _sinkContext = new SinkContext();
@@ -93,7 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server
_commandProcessingTimeEstimator = null; _commandProcessingTimeEstimator = null;
_systemEvent = systemEvent; _systemEvent = systemEvent;
_behaviourContext = new BehaviourContext(); _behaviourInfo = new BehaviourInfo();
_totalElapsedTicksUpdating = 0; _totalElapsedTicksUpdating = 0;
_sessionId = 0; _sessionId = 0;
@@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Server
ulong appletResourceId, ulong appletResourceId,
IVirtualMemoryManager memoryManager) IVirtualMemoryManager memoryManager)
{ {
if (!BehaviourContext.CheckValidRevision(parameter.Revision)) if (!BehaviourInfo.CheckValidRevision(parameter.Revision))
{ {
return ResultCode.OperationFailed; return ResultCode.OperationFailed;
} }
@@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Renderer.Server
Debug.Assert(parameter.RenderingDevice == AudioRendererRenderingDevice.Dsp && parameter.ExecutionMode == AudioRendererExecutionMode.Auto); Debug.Assert(parameter.RenderingDevice == AudioRendererRenderingDevice.Dsp && parameter.ExecutionMode == AudioRendererExecutionMode.Auto);
Logger.Info?.Print(LogClass.AudioRenderer, $"Initializing with REV{BehaviourContext.GetRevisionNumber(parameter.Revision)}"); Logger.Info?.Print(LogClass.AudioRenderer, $"Initializing with REV{BehaviourInfo.GetRevisionNumber(parameter.Revision)}");
_behaviourContext.SetUserRevision(parameter.Revision); _behaviourInfo.SetUserRevision(parameter.Revision);
_sampleRate = parameter.SampleRate; _sampleRate = parameter.SampleRate;
_sampleCount = parameter.SampleCount; _sampleCount = parameter.SampleCount;
@@ -151,7 +151,7 @@ namespace Ryujinx.Audio.Renderer.Server
workBufferAllocator = new WorkBufferAllocator(workBufferMemory); workBufferAllocator = new WorkBufferAllocator(workBufferMemory);
PoolMapper poolMapper = new(processHandle, false); PoolMapper poolMapper = new(processHandle, false);
poolMapper.InitializeSystemPool(ref _dspMemoryPoolState, workBuffer, workBufferSize); poolMapper.InitializeSystemPool(ref _dspMemoryPoolInfo, workBuffer, workBufferSize);
_mixBuffer = workBufferAllocator.Allocate<float>(_sampleCount * (_voiceChannelCountMax + _mixBufferCount), 0x10); _mixBuffer = workBufferAllocator.Allocate<float>(_sampleCount * (_voiceChannelCountMax + _mixBufferCount), 0x10);
@@ -176,7 +176,7 @@ namespace Ryujinx.Audio.Renderer.Server
Memory<BiquadFilterState> splitterBqfStates = Memory<BiquadFilterState>.Empty; Memory<BiquadFilterState> splitterBqfStates = Memory<BiquadFilterState>.Empty;
if (_behaviourContext.IsBiquadFilterParameterForSplitterEnabled() && if (_behaviourInfo.IsBiquadFilterParameterForSplitterEnabled() &&
parameter.SplitterCount > 0 && parameter.SplitterCount > 0 &&
parameter.SplitterDestinationCount > 0) parameter.SplitterDestinationCount > 0)
{ {
@@ -191,23 +191,23 @@ namespace Ryujinx.Audio.Renderer.Server
} }
// Invalidate DSP cache on what was currently allocated with workBuffer. // Invalidate DSP cache on what was currently allocated with workBuffer.
AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset); AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolInfo.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
Debug.Assert((workBufferAllocator.Offset % Constants.BufferAlignment) == 0); Debug.Assert((workBufferAllocator.Offset % Constants.BufferAlignment) == 0);
Memory<VoiceState> voices = workBufferAllocator.Allocate<VoiceState>(parameter.VoiceCount, VoiceState.Alignment); Memory<VoiceInfo> voices = workBufferAllocator.Allocate<VoiceInfo>(parameter.VoiceCount, VoiceInfo.Alignment);
if (voices.IsEmpty) if (voices.IsEmpty)
{ {
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
foreach (ref VoiceState voice in voices.Span) foreach (ref VoiceInfo voice in voices.Span)
{ {
voice.Initialize(); voice.Initialize();
} }
// A pain to handle as we can't have VoiceState*, use indices to be a bit more safe // A pain to handle as we can't have VoiceInfo*, use indices to be a bit more safe
Memory<int> sortedVoices = workBufferAllocator.Allocate<int>(parameter.VoiceCount, 0x10); Memory<int> sortedVoices = workBufferAllocator.Allocate<int>(parameter.VoiceCount, 0x10);
if (sortedVoices.IsEmpty) if (sortedVoices.IsEmpty)
@@ -233,16 +233,16 @@ namespace Ryujinx.Audio.Renderer.Server
voiceChannelResource.IsUsed = false; voiceChannelResource.IsUsed = false;
} }
Memory<VoiceUpdateState> voiceUpdateStates = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align); Memory<VoiceState> voiceStates = workBufferAllocator.Allocate<VoiceState>(parameter.VoiceCount, VoiceState.Align);
if (voiceUpdateStates.IsEmpty) if (voiceStates.IsEmpty)
{ {
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
uint mixesCount = parameter.SubMixBufferCount + 1; uint mixesCount = parameter.SubMixBufferCount + 1;
Memory<MixState> mixes = workBufferAllocator.Allocate<MixState>(mixesCount, MixState.Alignment); Memory<MixInfo> mixes = workBufferAllocator.Allocate<MixInfo>(mixesCount, MixInfo.Alignment);
if (mixes.IsEmpty) if (mixes.IsEmpty)
{ {
@@ -251,18 +251,18 @@ namespace Ryujinx.Audio.Renderer.Server
if (parameter.EffectCount == 0) if (parameter.EffectCount == 0)
{ {
foreach (ref MixState mix in mixes.Span) foreach (ref MixInfo mix in mixes.Span)
{ {
mix = new MixState(Memory<int>.Empty, ref _behaviourContext); mix = new MixInfo(Memory<int>.Empty, ref _behaviourInfo);
} }
} }
else else
{ {
Memory<int> effectProcessingOrderArray = workBufferAllocator.Allocate<int>(parameter.EffectCount * mixesCount, 0x10); Memory<int> effectProcessingOrderArray = workBufferAllocator.Allocate<int>(parameter.EffectCount * mixesCount, 0x10);
foreach (ref MixState mix in mixes.Span) foreach (ref MixInfo mix in mixes.Span)
{ {
mix = new MixState(effectProcessingOrderArray[..(int)parameter.EffectCount], ref _behaviourContext); mix = new MixInfo(effectProcessingOrderArray[..(int)parameter.EffectCount], ref _behaviourInfo);
effectProcessingOrderArray = effectProcessingOrderArray[(int)parameter.EffectCount..]; effectProcessingOrderArray = effectProcessingOrderArray[(int)parameter.EffectCount..];
} }
@@ -271,20 +271,20 @@ namespace Ryujinx.Audio.Renderer.Server
// Initialize the final mix id // Initialize the final mix id
mixes.Span[0].MixId = Constants.FinalMixId; mixes.Span[0].MixId = Constants.FinalMixId;
Memory<int> sortedMixesState = workBufferAllocator.Allocate<int>(mixesCount, 0x10); Memory<int> sortedMixesInfo = workBufferAllocator.Allocate<int>(mixesCount, 0x10);
if (sortedMixesState.IsEmpty) if (sortedMixesInfo.IsEmpty)
{ {
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
// Clear memory (use -1 as it's an invalid index) // Clear memory (use -1 as it's an invalid index)
sortedMixesState.Span.Fill(-1); sortedMixesInfo.Span.Fill(-1);
Memory<byte> nodeStatesWorkBuffer = Memory<byte>.Empty; Memory<byte> nodeStatesWorkBuffer = Memory<byte>.Empty;
Memory<byte> edgeMatrixWorkBuffer = Memory<byte>.Empty; Memory<byte> edgeMatrixWorkBuffer = Memory<byte>.Empty;
if (_behaviourContext.IsSplitterSupported()) if (_behaviourInfo.IsSplitterSupported())
{ {
nodeStatesWorkBuffer = workBufferAllocator.Allocate((uint)NodeStates.GetWorkBufferSize((int)mixesCount), 1); nodeStatesWorkBuffer = workBufferAllocator.Allocate((uint)NodeStates.GetWorkBufferSize((int)mixesCount), 1);
edgeMatrixWorkBuffer = workBufferAllocator.Allocate((uint)EdgeMatrix.GetWorkBufferSize((int)mixesCount), 1); edgeMatrixWorkBuffer = workBufferAllocator.Allocate((uint)EdgeMatrix.GetWorkBufferSize((int)mixesCount), 1);
@@ -295,21 +295,21 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
_mixContext.Initialize(sortedMixesState, mixes, nodeStatesWorkBuffer, edgeMatrixWorkBuffer); _mixContext.Initialize(sortedMixesInfo, mixes, nodeStatesWorkBuffer, edgeMatrixWorkBuffer);
_memoryPools = workBufferAllocator.Allocate<MemoryPoolState>(_memoryPoolCount, MemoryPoolState.Alignment); _memoryPools = workBufferAllocator.Allocate<MemoryPoolInfo>(_memoryPoolCount, MemoryPoolInfo.Alignment);
if (_memoryPools.IsEmpty) if (_memoryPools.IsEmpty)
{ {
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
foreach (ref MemoryPoolState state in _memoryPools.Span) foreach (ref MemoryPoolInfo info in _memoryPools.Span)
{ {
state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); info = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu);
} }
if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator, splitterBqfStates)) if (!_splitterContext.Initialize(ref _behaviourInfo, ref parameter, workBufferAllocator, splitterBqfStates))
{ {
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
@@ -318,21 +318,21 @@ namespace Ryujinx.Audio.Renderer.Server
_upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount); _upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount);
_effectContext.Initialize(parameter.EffectCount, _behaviourContext.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0); _effectContext.Initialize(parameter.EffectCount, _behaviourInfo.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0);
_sinkContext.Initialize(parameter.SinkCount); _sinkContext.Initialize(parameter.SinkCount);
Memory<VoiceUpdateState> voiceUpdateStatesDsp = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align); Memory<VoiceState> voiceStatesDsp = workBufferAllocator.Allocate<VoiceState>(parameter.VoiceCount, VoiceState.Align);
if (voiceUpdateStatesDsp.IsEmpty) if (voiceStatesDsp.IsEmpty)
{ {
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
_voiceContext.Initialize(sortedVoices, voices, voiceChannelResources, voiceUpdateStates, voiceUpdateStatesDsp, parameter.VoiceCount); _voiceContext.Initialize(sortedVoices, voices, voiceChannelResources, voiceStates, voiceStatesDsp, parameter.VoiceCount);
if (parameter.PerformanceMetricFramesCount > 0) if (parameter.PerformanceMetricFramesCount > 0)
{ {
ulong performanceBufferSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref _behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC; ulong performanceBufferSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref _behaviourInfo) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
_performanceBuffer = workBufferAllocator.Allocate(performanceBufferSize, Constants.BufferAlignment); _performanceBuffer = workBufferAllocator.Allocate(performanceBufferSize, Constants.BufferAlignment);
@@ -341,7 +341,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall; return ResultCode.WorkBufferTooSmall;
} }
_performanceManager = PerformanceManager.Create(_performanceBuffer, ref parameter, _behaviourContext); _performanceManager = PerformanceManager.Create(_performanceBuffer, ref parameter, _behaviourInfo);
} }
else else
{ {
@@ -359,14 +359,14 @@ namespace Ryujinx.Audio.Renderer.Server
_elapsedFrameCount = 0; _elapsedFrameCount = 0;
_voiceDropParameter = 1.0f; _voiceDropParameter = 1.0f;
_commandProcessingTimeEstimator = _behaviourContext.GetCommandProcessingTimeEstimatorVersion() switch _commandProcessingTimeEstimator = _behaviourInfo.GetCommandProcessingTimeEstimatorVersion() switch
{ {
1 => new CommandProcessingTimeEstimatorVersion1(_sampleCount, _mixBufferCount), 1 => new CommandProcessingTimeEstimatorVersion1(_sampleCount, _mixBufferCount),
2 => new CommandProcessingTimeEstimatorVersion2(_sampleCount, _mixBufferCount), 2 => new CommandProcessingTimeEstimatorVersion2(_sampleCount, _mixBufferCount),
3 => new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount), 3 => new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount),
4 => new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount), 4 => new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount),
5 => new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount), 5 => new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount),
_ => throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}."), _ => throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourInfo.GetCommandProcessingTimeEstimatorVersion()}."),
}; };
return ResultCode.Success; return ResultCode.Success;
@@ -411,11 +411,11 @@ namespace Ryujinx.Audio.Renderer.Server
output.Span.Clear(); output.Span.Clear();
StateUpdater stateUpdater = new(input, output, _processHandle, _behaviourContext); StateUpdater stateUpdater = new(input, output, _processHandle, _behaviourInfo);
ResultCode result; ResultCode result;
result = stateUpdater.UpdateBehaviourContext(); result = stateUpdater.UpdateBehaviourInfo();
if (result != ResultCode.Success) if (result != ResultCode.Success)
{ {
@@ -436,9 +436,16 @@ namespace Ryujinx.Audio.Renderer.Server
return result; return result;
} }
PoolMapper poolMapper = new(_processHandle, _memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); PoolMapper poolMapper = new(_processHandle, _memoryPools, _behaviourInfo.IsMemoryPoolForceMappingEnabled());
result = stateUpdater.UpdateVoices(_voiceContext, poolMapper); if (_behaviourInfo.IsBiquadFilterParameterFloatSupported())
{
result = stateUpdater.UpdateVoices2(_voiceContext, poolMapper);
}
else
{
result = stateUpdater.UpdateVoices1(_voiceContext, poolMapper);
}
if (result != ResultCode.Success) if (result != ResultCode.Success)
{ {
@@ -452,7 +459,7 @@ namespace Ryujinx.Audio.Renderer.Server
return result; return result;
} }
if (_behaviourContext.IsSplitterSupported()) if (_behaviourInfo.IsSplitterSupported())
{ {
result = stateUpdater.UpdateSplitter(_splitterContext); result = stateUpdater.UpdateSplitter(_splitterContext);
@@ -490,7 +497,7 @@ namespace Ryujinx.Audio.Renderer.Server
return result; return result;
} }
if (_behaviourContext.IsElapsedFrameCountSupported()) if (_behaviourInfo.IsElapsedFrameCountSupported())
{ {
result = stateUpdater.UpdateRendererInfo(_elapsedFrameCount); result = stateUpdater.UpdateRendererInfo(_elapsedFrameCount);
@@ -557,7 +564,7 @@ namespace Ryujinx.Audio.Renderer.Server
break; break;
} }
ref VoiceState voice = ref _voiceContext.GetState(NodeIdHelper.GetBase(targetNodeId)); ref VoiceInfo voice = ref _voiceContext.GetState(NodeIdHelper.GetBase(targetNodeId));
if (voice.Priority == Constants.VoiceHighestPriority) if (voice.Priority == Constants.VoiceHighestPriority)
{ {
@@ -646,7 +653,7 @@ namespace Ryujinx.Audio.Renderer.Server
_voiceContext.UpdateForCommandGeneration(); _voiceContext.UpdateForCommandGeneration();
if (_behaviourContext.IsEffectInfoVersion2Supported()) if (_behaviourInfo.IsEffectInfoVersion2Supported())
{ {
_effectContext.UpdateResultStateForCommandGeneration(); _effectContext.UpdateResultStateForCommandGeneration();
} }
@@ -661,7 +668,7 @@ namespace Ryujinx.Audio.Renderer.Server
private int GetMaxAllocatedTimeForDsp() private int GetMaxAllocatedTimeForDsp()
{ {
return (int)(Constants.AudioProcessorMaxUpdateTimePerSessions * _behaviourContext.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f)); return (int)(Constants.AudioProcessorMaxUpdateTimePerSessions * _behaviourInfo.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f));
} }
public void SendCommands() public void SendCommands()
@@ -736,7 +743,7 @@ namespace Ryujinx.Audio.Renderer.Server
return new RendererSystemContext return new RendererSystemContext
{ {
ChannelCount = _manager.Processor.OutputDevices[_sessionId].GetChannelCount(), ChannelCount = _manager.Processor.OutputDevices[_sessionId].GetChannelCount(),
BehaviourContext = _behaviourContext, BehaviourInfo = _behaviourInfo,
DepopBuffer = _depopBuffer, DepopBuffer = _depopBuffer,
MixBufferCount = GetMixBufferCount(), MixBufferCount = GetMixBufferCount(),
SessionId = _sessionId, SessionId = _sessionId,
@@ -751,9 +758,9 @@ namespace Ryujinx.Audio.Renderer.Server
public static ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter) public static ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter)
{ {
BehaviourContext behaviourContext = new(); BehaviourInfo behaviourInfo = new();
behaviourContext.SetUserRevision(parameter.Revision); behaviourInfo.SetUserRevision(parameter.Revision);
uint mixesCount = parameter.SubMixBufferCount + 1; uint mixesCount = parameter.SubMixBufferCount + 1;
@@ -771,28 +778,28 @@ namespace Ryujinx.Audio.Renderer.Server
size = WorkBufferAllocator.GetTargetSize<float>(size, BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment); size = WorkBufferAllocator.GetTargetSize<float>(size, BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
// Voice // Voice
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment); size = WorkBufferAllocator.GetTargetSize<VoiceInfo>(size, parameter.VoiceCount, VoiceInfo.Alignment);
size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.VoiceCount, 0x10); size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.VoiceCount, 0x10);
size = WorkBufferAllocator.GetTargetSize<VoiceChannelResource>(size, parameter.VoiceCount, VoiceChannelResource.Alignment); size = WorkBufferAllocator.GetTargetSize<VoiceChannelResource>(size, parameter.VoiceCount, VoiceChannelResource.Alignment);
size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align); size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Align);
// Mix // Mix
size = WorkBufferAllocator.GetTargetSize<MixState>(size, mixesCount, MixState.Alignment); size = WorkBufferAllocator.GetTargetSize<MixInfo>(size, mixesCount, MixInfo.Alignment);
size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.EffectCount * mixesCount, 0x10); size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.EffectCount * mixesCount, 0x10);
size = WorkBufferAllocator.GetTargetSize<int>(size, mixesCount, 0x10); size = WorkBufferAllocator.GetTargetSize<int>(size, mixesCount, 0x10);
if (behaviourContext.IsSplitterSupported()) if (behaviourInfo.IsSplitterSupported())
{ {
size += (ulong)BitUtils.AlignUp(NodeStates.GetWorkBufferSize((int)mixesCount) + EdgeMatrix.GetWorkBufferSize((int)mixesCount), 0x10); size += (ulong)BitUtils.AlignUp(NodeStates.GetWorkBufferSize((int)mixesCount) + EdgeMatrix.GetWorkBufferSize((int)mixesCount), 0x10);
} }
// Memory Pool // Memory Pool
size = WorkBufferAllocator.GetTargetSize<MemoryPoolState>(size, memoryPoolCount, MemoryPoolState.Alignment); size = WorkBufferAllocator.GetTargetSize<MemoryPoolInfo>(size, memoryPoolCount, MemoryPoolInfo.Alignment);
// Splitter // Splitter
size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter); size = SplitterContext.GetWorkBufferSize(size, ref behaviourInfo, ref parameter);
if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled() && if (behaviourInfo.IsBiquadFilterParameterForSplitterEnabled() &&
parameter.SplitterCount > 0 && parameter.SplitterCount > 0 &&
parameter.SplitterDestinationCount > 0) parameter.SplitterDestinationCount > 0)
{ {
@@ -800,12 +807,12 @@ namespace Ryujinx.Audio.Renderer.Server
} }
// DSP Voice // DSP Voice
size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align); size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Align);
// Performance // Performance
if (parameter.PerformanceMetricFramesCount > 0) if (parameter.PerformanceMetricFramesCount > 0)
{ {
ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC; ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourInfo) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
size += BitUtils.AlignUp<ulong>(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment); size += BitUtils.AlignUp<ulong>(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
} }
@@ -847,13 +854,13 @@ namespace Ryujinx.Audio.Renderer.Server
} }
PoolMapper mapper = new(_processHandle, false); PoolMapper mapper = new(_processHandle, false);
mapper.Unmap(ref _dspMemoryPoolState); mapper.Unmap(ref _dspMemoryPoolInfo);
PoolMapper.ClearUsageState(_memoryPools); PoolMapper.ClearUsageState(_memoryPools);
for (int i = 0; i < _memoryPoolCount; i++) for (int i = 0; i < _memoryPoolCount; i++)
{ {
ref MemoryPoolState memoryPool = ref _memoryPools.Span[i]; ref MemoryPoolInfo memoryPool = ref _memoryPools.Span[i];
if (memoryPool.IsMapped()) if (memoryPool.IsMapped())
{ {
@@ -875,7 +882,7 @@ namespace Ryujinx.Audio.Renderer.Server
public void SetVoiceDropParameter(float voiceDropParameter) public void SetVoiceDropParameter(float voiceDropParameter)
{ {
_voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 2.0f); _voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 4.0f);
} }
public float GetVoiceDropParameter() public float GetVoiceDropParameter()

View File

@@ -1,3 +1,5 @@
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
@@ -9,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// Behaviour context. /// Behaviour context.
/// </summary> /// </summary>
/// <remarks>This handles features based on the audio renderer revision provided by the user.</remarks> /// <remarks>This handles features based on the audio renderer revision provided by the user.</remarks>
public class BehaviourContext public class BehaviourInfo
{ {
/// <summary> /// <summary>
/// The base magic of the Audio Renderer revision. /// The base magic of the Audio Renderer revision.
@@ -40,7 +42,7 @@ namespace Ryujinx.Audio.Renderer.Server
public const int Revision4 = 4 << 24; public const int Revision4 = 4 << 24;
/// <summary> /// <summary>
/// REV5: <see cref="Parameter.VoiceInParameter.DecodingBehaviour"/>, <see cref="Parameter.VoiceInParameter.FlushWaveBufferCount"/> were added to voice. /// REV5: <see cref="VoiceInParameter1.DecodingBehaviour"/>, <see cref="VoiceInParameter1.FlushWaveBufferCount"/> were added to voice.
/// A new performance frame format (version 2) was added with support for more information about DSP timing. /// A new performance frame format (version 2) was added with support for more information about DSP timing.
/// <see cref="Parameter.RendererInfoOutStatus"/> was added to supply the count of update done sent to the DSP. /// <see cref="Parameter.RendererInfoOutStatus"/> was added to supply the count of update done sent to the DSP.
/// A new version of the command estimator was added to address timing changes caused by the voice changes. /// A new version of the command estimator was added to address timing changes caused by the voice changes.
@@ -64,7 +66,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// REV8: /// REV8:
/// Wavebuffer was changed to support more control over loop (you can now specify where to start and end a loop, and how many times to loop). /// Wavebuffer was changed to support more control over loop (you can now specify where to start and end a loop, and how many times to loop).
/// <see cref="Parameter.VoiceInParameter.SrcQuality"/> was added (see <see cref="Parameter.VoiceInParameter.SampleRateConversionQuality"/> for more info). /// <see cref="VoiceInParameter1.SrcQuality"/> was added (see <see cref="VoiceInParameter1.SampleRateConversionQuality"/> for more info).
/// Final leftovers of the codec system were removed. /// Final leftovers of the codec system were removed.
/// <see cref="Common.SampleFormat.PcmFloat"/> support was added. /// <see cref="Common.SampleFormat.PcmFloat"/> support was added.
/// A new version of the command estimator was added to address timing changes caused by the voice and command changes. /// A new version of the command estimator was added to address timing changes caused by the voice and command changes.
@@ -115,16 +117,27 @@ namespace Ryujinx.Audio.Renderer.Server
/// </summary> /// </summary>
/// <remarks>This was added in system update 18.0.0</remarks> /// <remarks>This was added in system update 18.0.0</remarks>
public const int Revision13 = 13 << 24; public const int Revision13 = 13 << 24;
/// <summary>
/// REV14:
/// Fixes the Depop Bug.
///
/// </summary>
/// <remarks>This was added in system update 19.0.0 </remarks>
public const int Revision14 = 14 << 24;
/// <summary>
/// REV15:
/// Support for float coefficients in biquad filters
///
/// </summary>
/// <remarks>This was added in system update 19.0.0 </remarks>
public const int Revision15 = 15 << 24;
/// <summary> /// <summary>
/// Last revision supported by the implementation. /// Last revision supported by the implementation.
/// </summary> /// </summary>
public const int LastRevision = Revision13; public const int LastRevision = Revision15;
/// <summary>
/// Target revision magic supported by the implementation.
/// </summary>
public const int ProcessRevision = BaseRevisionMagic + LastRevision;
/// <summary> /// <summary>
/// Get the revision number from the revision magic. /// Get the revision number from the revision magic.
@@ -133,15 +146,25 @@ namespace Ryujinx.Audio.Renderer.Server
/// <returns>The revision number.</returns> /// <returns>The revision number.</returns>
public static int GetRevisionNumber(int revision) => (revision - BaseRevisionMagic) >> 24; public static int GetRevisionNumber(int revision) => (revision - BaseRevisionMagic) >> 24;
/// <summary>
/// Target revision magic supported by the implementation.
/// </summary>
public const int ProcessRevision = BaseRevisionMagic + LastRevision;
/// <summary> /// <summary>
/// Current active revision. /// Current active revision.
/// </summary> /// </summary>
public int UserRevision { get; private set; } public int UserRevision { get; private set; }
/// <summary>
/// Current flags of the <see cref="BehaviourInfo"/>.
/// </summary>
private ulong _flags;
/// <summary> /// <summary>
/// Error storage. /// Error storage.
/// </summary> /// </summary>
private readonly ErrorInfo[] _errorInfos; private readonly Array10<ErrorInfo> _errorInfos;
/// <summary> /// <summary>
/// Current position in the <see cref="_errorInfos"/> array. /// Current position in the <see cref="_errorInfos"/> array.
@@ -149,17 +172,12 @@ namespace Ryujinx.Audio.Renderer.Server
private uint _errorIndex; private uint _errorIndex;
/// <summary> /// <summary>
/// Current flags of the <see cref="BehaviourContext"/>. /// Create a new instance of <see cref="BehaviourInfo"/>.
/// </summary> /// </summary>
private ulong _flags; public BehaviourInfo()
/// <summary>
/// Create a new instance of <see cref="BehaviourContext"/>.
/// </summary>
public BehaviourContext()
{ {
UserRevision = 0; UserRevision = 0;
_errorInfos = new ErrorInfo[Constants.MaxErrorInfos]; _errorInfos = new Array10<ErrorInfo>();
_errorIndex = 0; _errorIndex = 0;
} }
@@ -173,7 +191,7 @@ namespace Ryujinx.Audio.Renderer.Server
} }
/// <summary> /// <summary>
/// Update flags of the <see cref="BehaviourContext"/>. /// Update flags of the <see cref="BehaviourInfo"/>.
/// </summary> /// </summary>
/// <param name="flags">The new flags.</param> /// <param name="flags">The new flags.</param>
public void UpdateFlags(ulong flags) public void UpdateFlags(ulong flags)
@@ -321,9 +339,9 @@ namespace Ryujinx.Audio.Renderer.Server
} }
/// <summary> /// <summary>
/// Check if the audio renderer should support <see cref="Parameter.VoiceInParameter.DecodingBehaviour"/>. /// Check if the audio renderer should support <see cref="VoiceInParameter1.DecodingBehaviour"/>.
/// </summary> /// </summary>
/// <returns>True if the audio renderer should support <see cref="Parameter.VoiceInParameter.DecodingBehaviour"/>.</returns> /// <returns>True if the audio renderer should support <see cref="VoiceInParameter1.DecodingBehaviour"/>.</returns>
public bool IsDecodingBehaviourFlagSupported() public bool IsDecodingBehaviourFlagSupported()
{ {
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision5); return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision5);
@@ -400,6 +418,24 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13); return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13);
} }
/// <summary>
/// Check if the audio renderer should support the depop bug fix.
/// </summary>
/// <returns>True if the audio renderer supports the depop bug fix</returns>
public bool IsSplitterDepopBugFixEnabled()
{
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision14);
}
/// <summary>
/// Check if the audio renderer should support biquad filter with float coefficients.
/// </summary>
/// <returns>True if the audio renderer support biquad filter with float coefficients</returns>
public bool IsBiquadFilterParameterFloatSupported()
{
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision15);
}
/// <summary> /// <summary>
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>. /// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
@@ -440,7 +476,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (_errorIndex <= Constants.MaxErrorInfos - 1) if (_errorIndex <= Constants.MaxErrorInfos - 1)
{ {
_errorInfos[_errorIndex++] = errorInfo; _errorInfos[(int)_errorIndex++] = errorInfo;
} }
} }
@@ -457,22 +493,8 @@ namespace Ryujinx.Audio.Renderer.Server
} }
errorCount = Math.Min(_errorIndex, Constants.MaxErrorInfos); errorCount = Math.Min(_errorIndex, Constants.MaxErrorInfos);
for (int i = 0; i < Constants.MaxErrorInfos; i++) _errorInfos.AsSpan().CopyTo(errorInfos);
{
if (i < errorCount)
{
errorInfos[i] = _errorInfos[i];
}
else
{
errorInfos[i] = new ErrorInfo
{
ErrorCode = 0,
ExtraErrorInfo = 0,
};
}
}
} }
/// <summary> /// <summary>

View File

@@ -5,9 +5,11 @@ using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Parameter.Effect;
using Ryujinx.Audio.Renderer.Server.Performance; using Ryujinx.Audio.Renderer.Server.Performance;
using Ryujinx.Audio.Renderer.Server.Sink; using Ryujinx.Audio.Renderer.Server.Sink;
using Ryujinx.Audio.Renderer.Server.Splitter;
using Ryujinx.Audio.Renderer.Server.Upsampler; using Ryujinx.Audio.Renderer.Server.Upsampler;
using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Server.Voice;
using System; using System;
using System.Runtime.CompilerServices;
using CpuAddress = System.UInt64; using CpuAddress = System.UInt64;
namespace Ryujinx.Audio.Renderer.Server namespace Ryujinx.Audio.Renderer.Server
@@ -77,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="bufferOffset">The target buffer offset.</param> /// <param name="bufferOffset">The target buffer offset.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
/// <param name="wasPlaying">Set to true if the voice was playing previously.</param> /// <param name="wasPlaying">Set to true if the voice was playing previously.</param>
public void GenerateDepopPrepare(Memory<VoiceUpdateState> state, Memory<float> depopBuffer, uint bufferCount, uint bufferOffset, int nodeId, bool wasPlaying) public void GenerateDepopPrepare(Memory<VoiceState> state, Memory<float> depopBuffer, uint bufferCount, uint bufferOffset, int nodeId, bool wasPlaying)
{ {
DepopPrepareCommand command = new(state, depopBuffer, bufferCount, bufferOffset, nodeId, wasPlaying); DepopPrepareCommand command = new(state, depopBuffer, bufferCount, bufferOffset, nodeId, wasPlaying);
@@ -120,14 +122,14 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// Create a new <see cref="DataSourceVersion2Command"/>. /// Create a new <see cref="DataSourceVersion2Command"/>.
/// </summary> /// </summary>
/// <param name="voiceState">The <see cref="VoiceState"/> to generate the command from.</param> /// <param name="voiceInfo">The <see cref="VoiceInfo"/> to generate the command from.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="outputBufferIndex">The output buffer index to use.</param> /// <param name="outputBufferIndex">The output buffer index to use.</param>
/// <param name="channelIndex">The target channel index.</param> /// <param name="channelIndex">The target channel index.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateDataSourceVersion2(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) public void GenerateDataSourceVersion2(ref VoiceInfo voiceInfo, Memory<VoiceState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId)
{ {
DataSourceVersion2Command command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); DataSourceVersion2Command command = new(ref voiceInfo, state, outputBufferIndex, channelIndex, nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
@@ -137,14 +139,14 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// Create a new <see cref="PcmInt16DataSourceCommandVersion1"/>. /// Create a new <see cref="PcmInt16DataSourceCommandVersion1"/>.
/// </summary> /// </summary>
/// <param name="voiceState">The <see cref="VoiceState"/> to generate the command from.</param> /// <param name="voiceInfo">The <see cref="VoiceInfo"/> to generate the command from.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="outputBufferIndex">The output buffer index to use.</param> /// <param name="outputBufferIndex">The output buffer index to use.</param>
/// <param name="channelIndex">The target channel index.</param> /// <param name="channelIndex">The target channel index.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GeneratePcmInt16DataSourceVersion1(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) public void GeneratePcmInt16DataSourceVersion1(ref VoiceInfo voiceInfo, Memory<VoiceState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId)
{ {
PcmInt16DataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); PcmInt16DataSourceCommandVersion1 command = new(ref voiceInfo, state, outputBufferIndex, channelIndex, nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
@@ -154,14 +156,14 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// Create a new <see cref="PcmFloatDataSourceCommandVersion1"/>. /// Create a new <see cref="PcmFloatDataSourceCommandVersion1"/>.
/// </summary> /// </summary>
/// <param name="voiceState">The <see cref="VoiceState"/> to generate the command from.</param> /// <param name="voiceInfo">The <see cref="VoiceInfo"/> to generate the command from.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="outputBufferIndex">The output buffer index to use.</param> /// <param name="outputBufferIndex">The output buffer index to use.</param>
/// <param name="channelIndex">The target channel index.</param> /// <param name="channelIndex">The target channel index.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GeneratePcmFloatDataSourceVersion1(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) public void GeneratePcmFloatDataSourceVersion1(ref VoiceInfo voiceInfo, Memory<VoiceState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId)
{ {
PcmFloatDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); PcmFloatDataSourceCommandVersion1 command = new(ref voiceInfo, state, outputBufferIndex, channelIndex, nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
@@ -171,13 +173,13 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// Create a new <see cref="AdpcmDataSourceCommandVersion1"/>. /// Create a new <see cref="AdpcmDataSourceCommandVersion1"/>.
/// </summary> /// </summary>
/// <param name="voiceState">The <see cref="VoiceState"/> to generate the command from.</param> /// <param name="voiceInfo">The <see cref="VoiceInfo"/> to generate the command from.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="outputBufferIndex">The output buffer index to use.</param> /// <param name="outputBufferIndex">The output buffer index to use.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateAdpcmDataSourceVersion1(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, int nodeId) public void GenerateAdpcmDataSourceVersion1(ref VoiceInfo voiceInfo, Memory<VoiceState> state, ushort outputBufferIndex, int nodeId)
{ {
AdpcmDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, nodeId); AdpcmDataSourceCommandVersion1 command = new(ref voiceInfo, state, outputBufferIndex, nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
@@ -194,7 +196,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="outputBufferOffset">The output buffer offset.</param> /// <param name="outputBufferOffset">The output buffer offset.</param>
/// <param name="needInitialization">Set to true if the biquad filter state needs to be initialized.</param> /// <param name="needInitialization">Set to true if the biquad filter state needs to be initialized.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateBiquadFilter(int baseIndex, ref BiquadFilterParameter filter, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId) public void GenerateBiquadFilter(int baseIndex, ref BiquadFilterParameter2 filter, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId)
{ {
BiquadFilterCommand command = new(baseIndex, ref filter, biquadFilterStateMemory, inputBufferOffset, outputBufferOffset, needInitialization, nodeId); BiquadFilterCommand command = new(baseIndex, ref filter, biquadFilterStateMemory, inputBufferOffset, outputBufferOffset, needInitialization, nodeId);
@@ -213,7 +215,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="outputBufferOffset">The output buffer offset.</param> /// <param name="outputBufferOffset">The output buffer offset.</param>
/// <param name="isInitialized">Set to true if the biquad filter state is initialized.</param> /// <param name="isInitialized">Set to true if the biquad filter state is initialized.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId) public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter2> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
{ {
MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId);
@@ -230,9 +232,9 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="outputBufferIndex">The base output index.</param> /// <param name="outputBufferIndex">The base output index.</param>
/// <param name="previousVolume">The previous volume.</param> /// <param name="previousVolume">The previous volume.</param>
/// <param name="volume">The new volume.</param> /// <param name="volume">The new volume.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan<float> previousVolume, ReadOnlySpan<float> volume, Memory<VoiceUpdateState> state, int nodeId) public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan<float> previousVolume, ReadOnlySpan<float> volume, Memory<VoiceState> state, int nodeId)
{ {
MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId); MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId);
@@ -248,10 +250,10 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="volume">The new volume.</param> /// <param name="volume">The new volume.</param>
/// <param name="inputBufferIndex">The input buffer index.</param> /// <param name="inputBufferIndex">The input buffer index.</param>
/// <param name="outputBufferIndex">The output buffer index.</param> /// <param name="outputBufferIndex">The output buffer index.</param>
/// <param name="lastSampleIndex">The index in the <see cref="VoiceUpdateState.LastSamples"/> array to store the ramped sample.</param> /// <param name="lastSampleIndex">The index in the <see cref="VoiceState.LastSamples"/> array to store the ramped sample.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateMixRamp(float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory<VoiceUpdateState> state, int nodeId) public void GenerateMixRamp(float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory<VoiceState> state, int nodeId)
{ {
MixRampCommand command = new(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, nodeId); MixRampCommand command = new(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, nodeId);
@@ -267,8 +269,8 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="volume">The new volume.</param> /// <param name="volume">The new volume.</param>
/// <param name="inputBufferIndex">The input buffer index.</param> /// <param name="inputBufferIndex">The input buffer index.</param>
/// <param name="outputBufferIndex">The output buffer index.</param> /// <param name="outputBufferIndex">The output buffer index.</param>
/// <param name="lastSampleIndex">The index in the <see cref="VoiceUpdateState.LastSamples"/> array to store the ramped sample.</param> /// <param name="lastSampleIndex">The index in the <see cref="VoiceState.LastSamples"/> array to store the ramped sample.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="filter">The biquad filter parameter.</param> /// <param name="filter">The biquad filter parameter.</param>
/// <param name="biquadFilterState">The biquad state.</param> /// <param name="biquadFilterState">The biquad state.</param>
/// <param name="previousBiquadFilterState">The previous biquad state.</param> /// <param name="previousBiquadFilterState">The previous biquad state.</param>
@@ -282,8 +284,8 @@ namespace Ryujinx.Audio.Renderer.Server
uint inputBufferIndex, uint inputBufferIndex,
uint outputBufferIndex, uint outputBufferIndex,
int lastSampleIndex, int lastSampleIndex,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
ref BiquadFilterParameter filter, ref BiquadFilterParameter2 filter,
Memory<BiquadFilterState> biquadFilterState, Memory<BiquadFilterState> biquadFilterState,
Memory<BiquadFilterState> previousBiquadFilterState, Memory<BiquadFilterState> previousBiquadFilterState,
bool needInitialization, bool needInitialization,
@@ -318,8 +320,8 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="volume">The new volume.</param> /// <param name="volume">The new volume.</param>
/// <param name="inputBufferIndex">The input buffer index.</param> /// <param name="inputBufferIndex">The input buffer index.</param>
/// <param name="outputBufferIndex">The output buffer index.</param> /// <param name="outputBufferIndex">The output buffer index.</param>
/// <param name="lastSampleIndex">The index in the <see cref="VoiceUpdateState.LastSamples"/> array to store the ramped sample.</param> /// <param name="lastSampleIndex">The index in the <see cref="VoiceState.LastSamples"/> array to store the ramped sample.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param> /// <param name="state">The <see cref="VoiceState"/> to generate the command from.</param>
/// <param name="filter0">First biquad filter parameter.</param> /// <param name="filter0">First biquad filter parameter.</param>
/// <param name="filter1">Second biquad filter parameter.</param> /// <param name="filter1">Second biquad filter parameter.</param>
/// <param name="biquadFilterState0">First biquad state.</param> /// <param name="biquadFilterState0">First biquad state.</param>
@@ -337,9 +339,9 @@ namespace Ryujinx.Audio.Renderer.Server
uint inputBufferIndex, uint inputBufferIndex,
uint outputBufferIndex, uint outputBufferIndex,
int lastSampleIndex, int lastSampleIndex,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
ref BiquadFilterParameter filter0, ref BiquadFilterParameter2 filter0,
ref BiquadFilterParameter filter1, ref BiquadFilterParameter2 filter1,
Memory<BiquadFilterState> biquadFilterState0, Memory<BiquadFilterState> biquadFilterState0,
Memory<BiquadFilterState> biquadFilterState1, Memory<BiquadFilterState> biquadFilterState1,
Memory<BiquadFilterState> previousBiquadFilterState0, Memory<BiquadFilterState> previousBiquadFilterState0,
@@ -654,14 +656,14 @@ namespace Ryujinx.Audio.Renderer.Server
/// Create a new <see cref="UpsampleCommand"/>. /// Create a new <see cref="UpsampleCommand"/>.
/// </summary> /// </summary>
/// <param name="bufferOffset">The offset of the mix buffer.</param> /// <param name="bufferOffset">The offset of the mix buffer.</param>
/// <param name="upsampler">The <see cref="UpsamplerState"/> associated.</param> /// <param name="upsampler">The <see cref="UpsamplerInfo"/> associated.</param>
/// <param name="inputCount">The total input count.</param> /// <param name="inputCount">The total input count.</param>
/// <param name="inputBufferOffset">The input buffer mix offset.</param> /// <param name="inputBufferOffset">The input buffer mix offset.</param>
/// <param name="bufferCountPerSample">The buffer count per sample.</param> /// <param name="bufferCountPerSample">The buffer count per sample.</param>
/// <param name="sampleCount">The source sample count.</param> /// <param name="sampleCount">The source sample count.</param>
/// <param name="sampleRate">The source sample rate.</param> /// <param name="sampleRate">The source sample rate.</param>
/// <param name="nodeId">The node id associated to this command.</param> /// <param name="nodeId">The node id associated to this command.</param>
public void GenerateUpsample(uint bufferOffset, UpsamplerState upsampler, uint inputCount, Span<byte> inputBufferOffset, uint bufferCountPerSample, uint sampleCount, uint sampleRate, int nodeId) public void GenerateUpsample(uint bufferOffset, UpsamplerInfo upsampler, uint inputCount, Span<byte> inputBufferOffset, uint bufferCountPerSample, uint sampleCount, uint sampleRate, int nodeId)
{ {
UpsampleCommand command = new(bufferOffset, upsampler, inputCount, inputBufferOffset, bufferCountPerSample, sampleCount, sampleRate, nodeId); UpsampleCommand command = new(bufferOffset, upsampler, inputCount, inputBufferOffset, bufferCountPerSample, sampleCount, sampleRate, nodeId);
@@ -686,5 +688,23 @@ namespace Ryujinx.Audio.Renderer.Server
AddCommand(command); AddCommand(command);
} }
public void GenerateFillBuffer(SplitterDestination destination, float value, int length, int nodeId)
{
FillBufferCommand command;
if (Unsafe.IsNullRef(ref destination.GetV2RefOrNull()))
{
command = new(destination.GetV1RefOrNull(), length, value, nodeId);
}
else
{
command = new(destination.GetV2RefOrNull(), length, value, nodeId);
}
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
AddCommand(command);
}
} }
} }

View File

@@ -1,5 +1,6 @@
using Ryujinx.Audio.Common; using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Dsp.Command; using Ryujinx.Audio.Renderer.Dsp.Command;
using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter;
@@ -41,27 +42,27 @@ namespace Ryujinx.Audio.Renderer.Server
_commandBuffer.GenerateClearMixBuffer(Constants.InvalidNodeId); _commandBuffer.GenerateClearMixBuffer(Constants.InvalidNodeId);
} }
private void GenerateDataSource(ref VoiceState voiceState, Memory<VoiceUpdateState> dspState, int channelIndex) private void GenerateDataSource(ref VoiceInfo voiceInfo, Memory<VoiceState> dspState, int channelIndex)
{ {
if (voiceState.MixId != Constants.UnusedMixId) if (voiceInfo.MixId != Constants.UnusedMixId)
{ {
ref MixState mix = ref _mixContext.GetState(voiceState.MixId); ref MixInfo mix = ref _mixContext.GetState(voiceInfo.MixId);
_commandBuffer.GenerateDepopPrepare( _commandBuffer.GenerateDepopPrepare(
dspState, dspState,
_rendererContext.DepopBuffer, _rendererContext.DepopBuffer,
mix.BufferCount, mix.BufferCount,
mix.BufferOffset, mix.BufferOffset,
voiceState.NodeId, voiceInfo.NodeId,
voiceState.WasPlaying); voiceInfo.WasPlaying);
} }
else if (voiceState.SplitterId != Constants.UnusedSplitterId) else if (voiceInfo.SplitterId != Constants.UnusedSplitterId)
{ {
int destinationId = 0; int destinationId = 0;
while (true) while (true)
{ {
SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++); SplitterDestination destination = _splitterContext.GetDestination((int)voiceInfo.SplitterId, destinationId++);
if (destination.IsNull) if (destination.IsNull)
{ {
@@ -74,15 +75,17 @@ namespace Ryujinx.Audio.Renderer.Server
if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt) if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
{ {
ref MixState mix = ref _mixContext.GetState(mixId); ref MixInfo mix = ref _mixContext.GetState(mixId);
// _commandBuffer.GenerateFillBuffer();
_commandBuffer.GenerateDepopPrepare( _commandBuffer.GenerateDepopPrepare(
dspState, dspState,
_rendererContext.DepopBuffer, _rendererContext.DepopBuffer,
mix.BufferCount, mix.BufferCount,
mix.BufferOffset, mix.BufferOffset,
voiceState.NodeId, voiceInfo.NodeId,
voiceState.WasPlaying); voiceInfo.WasPlaying);
destination.MarkAsNeedToUpdateInternalState(); destination.MarkAsNeedToUpdateInternalState();
} }
@@ -90,69 +93,71 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
if (!voiceState.WasPlaying) if (!voiceInfo.WasPlaying)
{ {
Debug.Assert(voiceState.SampleFormat != SampleFormat.Adpcm || channelIndex == 0); Debug.Assert(voiceInfo.SampleFormat != SampleFormat.Adpcm || channelIndex == 0);
if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported()) if (_rendererContext.BehaviourInfo.IsWaveBufferVersion2Supported())
{ {
_commandBuffer.GenerateDataSourceVersion2( _commandBuffer.GenerateDataSourceVersion2(
ref voiceState, ref voiceInfo,
dspState, dspState,
(ushort)_rendererContext.MixBufferCount, (ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex, (ushort)channelIndex,
voiceState.NodeId); voiceInfo.NodeId);
} }
else else
{ {
switch (voiceState.SampleFormat) switch (voiceInfo.SampleFormat)
{ {
case SampleFormat.PcmInt16: case SampleFormat.PcmInt16:
_commandBuffer.GeneratePcmInt16DataSourceVersion1( _commandBuffer.GeneratePcmInt16DataSourceVersion1(
ref voiceState, ref voiceInfo,
dspState, dspState,
(ushort)_rendererContext.MixBufferCount, (ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex, (ushort)channelIndex,
voiceState.NodeId); voiceInfo.NodeId);
break; break;
case SampleFormat.PcmFloat: case SampleFormat.PcmFloat:
_commandBuffer.GeneratePcmFloatDataSourceVersion1( _commandBuffer.GeneratePcmFloatDataSourceVersion1(
ref voiceState, ref voiceInfo,
dspState, dspState,
(ushort)_rendererContext.MixBufferCount, (ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex, (ushort)channelIndex,
voiceState.NodeId); voiceInfo.NodeId);
break; break;
case SampleFormat.Adpcm: case SampleFormat.Adpcm:
_commandBuffer.GenerateAdpcmDataSourceVersion1( _commandBuffer.GenerateAdpcmDataSourceVersion1(
ref voiceState, ref voiceInfo,
dspState, dspState,
(ushort)_rendererContext.MixBufferCount, (ushort)_rendererContext.MixBufferCount,
voiceState.NodeId); voiceInfo.NodeId);
break; break;
default: default:
throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}"); throw new NotImplementedException($"Unsupported data source {voiceInfo.SampleFormat}");
} }
} }
} }
} }
private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId) private void GenerateBiquadFilterForVoice(ref VoiceInfo voiceInfo, Memory<VoiceState> state, int baseIndex, int bufferOffset, int nodeId)
{ {
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing(); bool supportsOptimizedPath = _rendererContext.BehaviourInfo.UseMultiTapBiquadFilterProcessing();
if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable) Span<BiquadFilterParameter2> biquadFiltersSpan = voiceInfo.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, voiceInfo.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 BiquadFilterParameter2 filter = ref biquadFiltersSpan[i];
if (filter.Enable) if (filter.Enable)
{ {
@@ -165,7 +170,7 @@ namespace Ryujinx.Audio.Renderer.Server
stateMemory.Slice(i, 1), stateMemory.Slice(i, 1),
bufferOffset, bufferOffset,
bufferOffset, bufferOffset,
!voiceState.BiquadFilterNeedInitialization[i], !voiceInfo.BiquadFilterNeedInitialization[i],
nodeId); nodeId);
} }
} }
@@ -174,7 +179,7 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateVoiceMixWithSplitter( private void GenerateVoiceMixWithSplitter(
SplitterDestination destination, SplitterDestination destination,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
uint bufferOffset, uint bufferOffset,
uint bufferCount, uint bufferCount,
uint bufferIndex, uint bufferIndex,
@@ -183,8 +188,8 @@ namespace Ryujinx.Audio.Renderer.Server
ReadOnlySpan<float> mixVolumes = destination.MixBufferVolume; ReadOnlySpan<float> mixVolumes = destination.MixBufferVolume;
ReadOnlySpan<float> previousMixVolumes = destination.PreviousMixBufferVolume; ReadOnlySpan<float> previousMixVolumes = destination.PreviousMixBufferVolume;
ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0); ref BiquadFilterParameter2 bqf0 = ref destination.GetBiquadFilterParameter(0);
ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1); ref BiquadFilterParameter2 bqf1 = ref destination.GetBiquadFilterParameter(1);
Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination); Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
@@ -268,7 +273,7 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateVoiceMix( private void GenerateVoiceMix(
ReadOnlySpan<float> mixVolumes, ReadOnlySpan<float> mixVolumes,
ReadOnlySpan<float> previousMixVolumes, ReadOnlySpan<float> previousMixVolumes,
Memory<VoiceUpdateState> state, Memory<VoiceState> state,
uint bufferOffset, uint bufferOffset,
uint bufferCount, uint bufferCount,
uint bufferIndex, uint bufferIndex,
@@ -307,24 +312,27 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
private void GenerateVoice(ref VoiceState voiceState) private void GenerateVoice(ref VoiceInfo voiceInfo)
{ {
int nodeId = voiceState.NodeId; int nodeId = voiceInfo.NodeId;
uint channelsCount = voiceState.ChannelsCount; uint channelsCount = voiceInfo.ChannelsCount;
Span<int> channelResourceIdsSpan = voiceInfo.ChannelResourceIds.AsSpan();
Span<BiquadFilterParameter2> biquadFiltersSpan = voiceInfo.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<VoiceState> 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;
if (voiceState.SampleFormat == SampleFormat.PcmInt16) if (voiceInfo.SampleFormat == SampleFormat.PcmInt16)
{ {
dataSourceDetailType = PerformanceDetailType.PcmInt16; dataSourceDetailType = PerformanceDetailType.PcmInt16;
} }
else if (voiceState.SampleFormat == SampleFormat.PcmFloat) else if (voiceInfo.SampleFormat == SampleFormat.PcmFloat)
{ {
dataSourceDetailType = PerformanceDetailType.PcmFloat; dataSourceDetailType = PerformanceDetailType.PcmFloat;
} }
@@ -340,18 +348,18 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
} }
GenerateDataSource(ref voiceState, dspStateMemory, channelIndex); GenerateDataSource(ref voiceInfo, dspStateMemory, channelIndex);
if (performanceInitialized) if (performanceInitialized)
{ {
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId); GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
} }
if (voiceState.WasPlaying) if (voiceInfo.WasPlaying)
{ {
voiceState.PreviousVolume = 0.0f; voiceInfo.PreviousVolume = 0.0f;
} }
else if (voiceState.HasAnyDestination()) else if (voiceInfo.HasAnyDestination())
{ {
performanceInitialized = false; performanceInitialized = false;
@@ -362,7 +370,7 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
} }
GenerateBiquadFilterForVoice(ref voiceState, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId); GenerateBiquadFilterForVoice(ref voiceInfo, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId);
if (performanceInitialized) if (performanceInitialized)
{ {
@@ -379,8 +387,8 @@ namespace Ryujinx.Audio.Renderer.Server
} }
_commandBuffer.GenerateVolumeRamp( _commandBuffer.GenerateVolumeRamp(
voiceState.PreviousVolume, voiceInfo.PreviousVolume,
voiceState.Volume, voiceInfo.Volume,
_rendererContext.MixBufferCount + (uint)channelIndex, _rendererContext.MixBufferCount + (uint)channelIndex,
nodeId); nodeId);
@@ -389,17 +397,17 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId); GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
} }
voiceState.PreviousVolume = voiceState.Volume; voiceInfo.PreviousVolume = voiceInfo.Volume;
if (voiceState.MixId == Constants.UnusedMixId) if (voiceInfo.MixId == Constants.UnusedMixId)
{ {
if (voiceState.SplitterId != Constants.UnusedSplitterId) if (voiceInfo.SplitterId != Constants.UnusedSplitterId)
{ {
int destinationId = channelIndex; int destinationId = channelIndex;
while (true) while (true)
{ {
SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId); SplitterDestination destination = _splitterContext.GetDestination((int)voiceInfo.SplitterId, destinationId);
if (destination.IsNull) if (destination.IsNull)
{ {
@@ -414,7 +422,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt) if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
{ {
ref MixState mix = ref _mixContext.GetState(mixId); ref MixInfo mix = ref _mixContext.GetState(mixId);
if (destination.IsBiquadFilterEnabled()) if (destination.IsBiquadFilterEnabled())
{ {
@@ -446,7 +454,7 @@ namespace Ryujinx.Audio.Renderer.Server
} }
else else
{ {
ref MixState mix = ref _mixContext.GetState(voiceState.MixId); ref MixInfo mix = ref _mixContext.GetState(voiceInfo.MixId);
performanceInitialized = false; performanceInitialized = false;
@@ -474,9 +482,9 @@ namespace Ryujinx.Audio.Renderer.Server
channelResource.UpdateState(); channelResource.UpdateState();
} }
for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++) for (int i = 0; i < voiceInfo.BiquadFilterNeedInitialization.Length; i++)
{ {
voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable; voiceInfo.BiquadFilterNeedInitialization[i] = biquadFiltersSpan[i].Enable;
} }
} }
} }
@@ -486,11 +494,11 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
for (int i = 0; i < _voiceContext.GetCount(); i++) for (int i = 0; i < _voiceContext.GetCount(); i++)
{ {
ref VoiceState sortedState = ref _voiceContext.GetSortedState(i); ref VoiceInfo sortedInfo = ref _voiceContext.GetSortedState(i);
if (!sortedState.ShouldSkip() && sortedState.UpdateForCommandGeneration(_voiceContext)) if (!sortedInfo.ShouldSkip() && sortedInfo.UpdateForCommandGeneration(_voiceContext))
{ {
int nodeId = sortedState.NodeId; int nodeId = sortedInfo.NodeId;
PerformanceEntryAddresses performanceEntry = new(); PerformanceEntryAddresses performanceEntry = new();
@@ -503,7 +511,7 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
} }
GenerateVoice(ref sortedState); GenerateVoice(ref sortedInfo);
if (performanceInitialized) if (performanceInitialized)
{ {
@@ -526,15 +534,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 +566,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 +587,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,13 +635,16 @@ 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)
{ {
bool needInitialization = effect.Parameter.Status == UsageState.Invalid || bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed()); (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourInfo.IsBiquadFilterEffectStateClearBugFixed());
BiquadFilterParameter parameter = new() BiquadFilterParameter2 parameter = new()
{ {
Enable = true, Enable = true,
}; };
@@ -639,8 +658,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 +668,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)
@@ -667,7 +686,7 @@ namespace Ryujinx.Audio.Renderer.Server
ulong workBuffer = effect.GetWorkBuffer(-1); ulong workBuffer = effect.GetWorkBuffer(-1);
if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported()) if (_rendererContext.BehaviourInfo.IsEffectInfoVersion2Supported())
{ {
Memory<EffectResultState> dspResultState; Memory<EffectResultState> dspResultState;
@@ -701,6 +720,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 +740,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,
@@ -759,7 +780,7 @@ namespace Ryujinx.Audio.Renderer.Server
nodeId); nodeId);
} }
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect) private void GenerateEffect(ref MixInfo mix, int effectId, BaseEffect effect)
{ {
int nodeId = mix.NodeId; int nodeId = mix.NodeId;
@@ -789,13 +810,13 @@ namespace Ryujinx.Audio.Renderer.Server
GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId); GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
break; break;
case EffectType.Delay: case EffectType.Delay:
GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported()); GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourInfo.IsNewEffectChannelMappingSupported());
break; break;
case EffectType.Reverb: case EffectType.Reverb:
GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported()); GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourInfo.IsNewEffectChannelMappingSupported());
break; break;
case EffectType.Reverb3d: case EffectType.Reverb3d:
GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported()); GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourInfo.IsNewEffectChannelMappingSupported());
break; break;
case EffectType.BiquadFilter: case EffectType.BiquadFilter:
GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId); GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
@@ -821,7 +842,7 @@ namespace Ryujinx.Audio.Renderer.Server
effect.UpdateForCommandGeneration(); effect.UpdateForCommandGeneration();
} }
private void GenerateEffects(ref MixState mix) private void GenerateEffects(ref MixInfo mix)
{ {
ReadOnlySpan<int> effectProcessingOrderArray = mix.EffectProcessingOrderArray; ReadOnlySpan<int> effectProcessingOrderArray = mix.EffectProcessingOrderArray;
@@ -857,8 +878,8 @@ namespace Ryujinx.Audio.Renderer.Server
ref bool isFirstMixBuffer, ref bool isFirstMixBuffer,
int nodeId) int nodeId)
{ {
ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0); ref BiquadFilterParameter2 bqf0 = ref destination.GetBiquadFilterParameter(0);
ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1); ref BiquadFilterParameter2 bqf1 = ref destination.GetBiquadFilterParameter(1);
Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination); Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
@@ -870,7 +891,7 @@ namespace Ryujinx.Audio.Renderer.Server
inputBufferIndex, inputBufferIndex,
outputBufferIndex, outputBufferIndex,
0, 0,
Memory<VoiceUpdateState>.Empty, Memory<VoiceState>.Empty,
ref bqf0, ref bqf0,
ref bqf1, ref bqf1,
bqfState[..1], bqfState[..1],
@@ -894,7 +915,7 @@ namespace Ryujinx.Audio.Renderer.Server
inputBufferIndex, inputBufferIndex,
outputBufferIndex, outputBufferIndex,
0, 0,
Memory<VoiceUpdateState>.Empty, Memory<VoiceState>.Empty,
ref bqf0, ref bqf0,
bqfState[..1], bqfState[..1],
bqfState.Slice(1, 1), bqfState.Slice(1, 1),
@@ -913,7 +934,7 @@ namespace Ryujinx.Audio.Renderer.Server
inputBufferIndex, inputBufferIndex,
outputBufferIndex, outputBufferIndex,
0, 0,
Memory<VoiceUpdateState>.Empty, Memory<VoiceState>.Empty,
ref bqf1, ref bqf1,
bqfState[..1], bqfState[..1],
bqfState.Slice(1, 1), bqfState.Slice(1, 1),
@@ -928,7 +949,7 @@ namespace Ryujinx.Audio.Renderer.Server
isFirstMixBuffer = false; isFirstMixBuffer = false;
} }
private void GenerateMix(ref MixState mix) private void GenerateMix(ref MixInfo mix)
{ {
if (mix.HasAnyDestination()) if (mix.HasAnyDestination())
{ {
@@ -957,7 +978,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt) if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
{ {
ref MixState destinationMix = ref _mixContext.GetState(mixId); ref MixInfo destinationMix = ref _mixContext.GetState(mixId);
uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount); uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
@@ -996,7 +1017,7 @@ namespace Ryujinx.Audio.Renderer.Server
} }
else else
{ {
ref MixState destinationMix = ref _mixContext.GetState(mix.DestinationMixId); ref MixInfo destinationMix = ref _mixContext.GetState(mix.DestinationMixId);
for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++) for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++)
{ {
@@ -1018,7 +1039,7 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
private void GenerateSubMix(ref MixState subMix) private void GenerateSubMix(ref MixInfo subMix)
{ {
_commandBuffer.GenerateDepopForMixBuffers( _commandBuffer.GenerateDepopForMixBuffers(
_rendererContext.DepopBuffer, _rendererContext.DepopBuffer,
@@ -1054,11 +1075,11 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
for (int id = 0; id < _mixContext.GetCount(); id++) for (int id = 0; id < _mixContext.GetCount(); id++)
{ {
ref MixState sortedState = ref _mixContext.GetSortedState(id); ref MixInfo sortedInfo = ref _mixContext.GetSortedState(id);
if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId) if (sortedInfo.IsUsed && sortedInfo.MixId != Constants.FinalMixId)
{ {
int nodeId = sortedState.NodeId; int nodeId = sortedInfo.NodeId;
PerformanceEntryAddresses performanceEntry = new(); PerformanceEntryAddresses performanceEntry = new();
@@ -1071,7 +1092,7 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
} }
GenerateSubMix(ref sortedState); GenerateSubMix(ref sortedInfo);
if (performanceInitialized) if (performanceInitialized)
{ {
@@ -1083,7 +1104,7 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateFinalMix() private void GenerateFinalMix()
{ {
ref MixState finalMix = ref _mixContext.GetFinalState(); ref MixInfo finalMix = ref _mixContext.GetFinalState();
_commandBuffer.GenerateDepopForMixBuffers( _commandBuffer.GenerateDepopForMixBuffers(
_rendererContext.DepopBuffer, _rendererContext.DepopBuffer,
@@ -1162,16 +1183,16 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix) private void GenerateCircularBuffer(CircularBufferSink sink, ref MixInfo finalMix)
{ {
_commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId); _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId);
} }
private void GenerateDevice(DeviceSink sink, ref MixState finalMix) private void GenerateDevice(DeviceSink sink, ref MixInfo finalMix)
{ {
if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerState == null) if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerInfo == null)
{ {
sink.UpsamplerState = _rendererContext.UpsamplerManager.Allocate(); sink.UpsamplerInfo = _rendererContext.UpsamplerManager.Allocate();
} }
bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled; bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled;
@@ -1198,11 +1219,11 @@ namespace Ryujinx.Audio.Renderer.Server
CommandList commandList = _commandBuffer.CommandList; CommandList commandList = _commandBuffer.CommandList;
if (sink.UpsamplerState != null) if (sink.UpsamplerInfo != null)
{ {
_commandBuffer.GenerateUpsample( _commandBuffer.GenerateUpsample(
finalMix.BufferOffset, finalMix.BufferOffset,
sink.UpsamplerState, sink.UpsamplerInfo,
sink.Parameter.InputCount, sink.Parameter.InputCount,
sink.Parameter.Input.AsSpan(), sink.Parameter.Input.AsSpan(),
commandList.BufferCount, commandList.BufferCount,
@@ -1219,7 +1240,7 @@ namespace Ryujinx.Audio.Renderer.Server
Constants.InvalidNodeId); Constants.InvalidNodeId);
} }
private void GenerateSink(BaseSink sink, ref MixState finalMix) private void GenerateSink(BaseSink sink, ref MixInfo finalMix)
{ {
bool performanceInitialized = false; bool performanceInitialized = false;
@@ -1257,7 +1278,7 @@ namespace Ryujinx.Audio.Renderer.Server
public void GenerateSinks() public void GenerateSinks()
{ {
ref MixState finalMix = ref _mixContext.GetFinalState(); ref MixInfo finalMix = ref _mixContext.GetFinalState();
for (int i = 0; i < _sinkContext.GetCount(); i++) for (int i = 0; i < _sinkContext.GetCount(); i++)
{ {

View File

@@ -194,5 +194,10 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return 0; return 0;
} }
public uint Estimate(FillBufferCommand command)
{
return 0;
}
} }
} }

View File

@@ -486,5 +486,10 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return 0; return 0;
} }
public uint Estimate(FillBufferCommand command)
{
return 0;
}
} }
} }

View File

@@ -3,7 +3,7 @@ using Ryujinx.Audio.Renderer.Dsp.Command;
using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Parameter.Effect;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using Ryujinx.Audio.Renderer.Parameter;
namespace Ryujinx.Audio.Renderer.Server namespace Ryujinx.Audio.Renderer.Server
{ {
@@ -656,5 +656,10 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return 0; return 0;
} }
public virtual uint Estimate(FillBufferCommand command)
{
return 0;
}
} }
} }

View File

@@ -286,5 +286,10 @@ namespace Ryujinx.Audio.Renderer.Server
return 8683; return 8683;
} }
} }
public override uint Estimate(FillBufferCommand command)
{
return 0;
}
} }
} }

View File

@@ -174,6 +174,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
updateErrorInfo = new ErrorInfo(); updateErrorInfo = new ErrorInfo();
} }
/// <summary>
/// Update the internal state from a user version 3 parameter.
/// </summary>
/// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
/// <param name="parameter">The user parameter.</param>
/// <param name="mapper">The mapper to use.</param>
public virtual void Update(out ErrorInfo updateErrorInfo, in EffectInParameterVersion3 parameter, PoolMapper mapper)
{
Debug.Assert(IsTypeValid(in parameter));
updateErrorInfo = new ErrorInfo();
}
/// <summary> /// <summary>
/// Get the work buffer DSP address at the given index. /// Get the work buffer DSP address at the given index.

View File

@@ -1,4 +1,5 @@
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Parameter.Effect;
@@ -17,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// <summary> /// <summary>
/// The biquad filter parameter. /// The biquad filter parameter.
/// </summary> /// </summary>
public BiquadFilterEffectParameter Parameter; public BiquadFilterEffectParameter2 Parameter;
/// <summary> /// <summary>
/// The biquad filter state. /// The biquad filter state.
@@ -29,7 +30,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// </summary> /// </summary>
public BiquadFilterEffect() public BiquadFilterEffect()
{ {
Parameter = new BiquadFilterEffectParameter(); Parameter = new BiquadFilterEffectParameter2();
State = new BiquadFilterState[Constants.ChannelCountMax]; State = new BiquadFilterState[Constants.ChannelCountMax];
} }
@@ -44,6 +45,11 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
{ {
Update(out updateErrorInfo, in parameter, mapper); Update(out updateErrorInfo, in parameter, mapper);
} }
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion3 parameter, PoolMapper mapper)
{
Update(out updateErrorInfo, in parameter, mapper);
}
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{ {
@@ -51,7 +57,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
UpdateParameterBase(in parameter); UpdateParameterBase(in parameter);
Parameter = MemoryMarshal.Cast<byte, BiquadFilterEffectParameter>(parameter.SpecificData)[0]; if (typeof(T) == typeof(EffectInParameterVersion3))
{
Parameter = MemoryMarshal.Cast<byte, BiquadFilterEffectParameter2>(parameter.SpecificData)[0];
}
else
{
BiquadFilterEffectParameter1 oldParameter =
MemoryMarshal.Cast<byte, BiquadFilterEffectParameter1>(parameter.SpecificData)[0];
Parameter = BiquadFilterHelper.ToBiquadFilterEffectParameter2(oldParameter);
}
IsEnabled = parameter.IsEnabled; IsEnabled = parameter.IsEnabled;
updateErrorInfo = new BehaviourParameter.ErrorInfo(); updateErrorInfo = new BehaviourParameter.ErrorInfo();

View File

@@ -38,5 +38,6 @@ namespace Ryujinx.Audio.Renderer.Server
uint Estimate(CompressorCommand command); uint Estimate(CompressorCommand command);
uint Estimate(BiquadFilterAndMixCommand command); uint Estimate(BiquadFilterAndMixCommand command);
uint Estimate(MultiTapBiquadFilterAndMixCommand command); uint Estimate(MultiTapBiquadFilterAndMixCommand command);
uint Estimate(FillBufferCommand command);
} }
} }

View File

@@ -20,14 +20,14 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
/// </summary> /// </summary>
public ulong Size; public ulong Size;
private unsafe MemoryPoolState* _memoryPools; private unsafe MemoryPoolInfo* _memoryPools;
/// <summary> /// <summary>
/// The forced DSP address of the region. /// The forced DSP address of the region.
/// </summary> /// </summary>
public DspAddress ForceMappedDspAddress; public DspAddress ForceMappedDspAddress;
private readonly unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; private readonly unsafe ref MemoryPoolInfo MemoryPoolInfo => ref *_memoryPools;
public readonly unsafe bool HasMemoryPoolState => (nint)_memoryPools != nint.Zero; public readonly unsafe bool HasMemoryPoolState => (nint)_memoryPools != nint.Zero;
@@ -53,7 +53,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
return new AddressInfo return new AddressInfo
{ {
CpuAddress = cpuAddress, CpuAddress = cpuAddress,
_memoryPools = MemoryPoolState.Null, _memoryPools = MemoryPoolInfo.Null,
Size = size, Size = size,
ForceMappedDspAddress = 0, ForceMappedDspAddress = 0,
}; };
@@ -73,19 +73,19 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
unsafe unsafe
{ {
_memoryPools = MemoryPoolState.Null; _memoryPools = MemoryPoolInfo.Null;
} }
} }
/// <summary> /// <summary>
/// Set the <see cref="MemoryPoolState"/> associated. /// Set the <see cref="MemoryPoolInfo"/> associated.
/// </summary> /// </summary>
/// <param name="memoryPoolState">The <see cref="MemoryPoolState"/> associated.</param> /// <param name="memoryPoolState">The <see cref="MemoryPoolInfo"/> associated.</param>
public void SetupMemoryPool(Span<MemoryPoolState> memoryPoolState) public void SetupMemoryPool(Span<MemoryPoolInfo> memoryPoolState)
{ {
unsafe unsafe
{ {
fixed (MemoryPoolState* ptr = &MemoryMarshal.GetReference(memoryPoolState)) fixed (MemoryPoolInfo* ptr = &MemoryMarshal.GetReference(memoryPoolState))
{ {
SetupMemoryPool(ptr); SetupMemoryPool(ptr);
} }
@@ -93,27 +93,27 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Set the <see cref="MemoryPoolState"/> associated. /// Set the <see cref="MemoryPoolInfo"/> associated.
/// </summary> /// </summary>
/// <param name="memoryPoolState">The <see cref="MemoryPoolState"/> associated.</param> /// <param name="memoryPoolState">The <see cref="MemoryPoolInfo"/> associated.</param>
public unsafe void SetupMemoryPool(MemoryPoolState* memoryPoolState) public unsafe void SetupMemoryPool(MemoryPoolInfo* memoryPoolState)
{ {
_memoryPools = memoryPoolState; _memoryPools = memoryPoolState;
} }
/// <summary> /// <summary>
/// Check if the <see cref="MemoryPoolState"/> is mapped. /// Check if the <see cref="MemoryPoolInfo"/> is mapped.
/// </summary> /// </summary>
/// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped.</returns> /// <returns>Returns true if the <see cref="MemoryPoolInfo"/> is mapped.</returns>
public readonly bool HasMappedMemoryPool() public readonly bool HasMappedMemoryPool()
{ {
return HasMemoryPoolState && MemoryPoolState.IsMapped(); return HasMemoryPoolState && MemoryPoolInfo.IsMapped();
} }
/// <summary> /// <summary>
/// Get the DSP address associated to the <see cref="AddressInfo"/>. /// Get the DSP address associated to the <see cref="AddressInfo"/>.
/// </summary> /// </summary>
/// <param name="markUsed">If true, mark the <see cref="MemoryPoolState"/> as used.</param> /// <param name="markUsed">If true, mark the <see cref="MemoryPoolInfo"/> as used.</param>
/// <returns>Returns the DSP address associated to the <see cref="AddressInfo"/>.</returns> /// <returns>Returns the DSP address associated to the <see cref="AddressInfo"/>.</returns>
public readonly DspAddress GetReference(bool markUsed) public readonly DspAddress GetReference(bool markUsed)
{ {
@@ -124,10 +124,10 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
if (markUsed) if (markUsed)
{ {
MemoryPoolState.IsUsed = true; MemoryPoolInfo.IsUsed = true;
} }
return MemoryPoolState.Translate(CpuAddress, Size); return MemoryPoolInfo.Translate(CpuAddress, Size);
} }
} }
} }

View File

@@ -8,62 +8,62 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
/// Server state for a memory pool. /// Server state for a memory pool.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)] [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)]
public struct MemoryPoolState public struct MemoryPoolInfo
{ {
public const int Alignment = 0x10; public const int Alignment = 0x10;
/// <summary> /// <summary>
/// The location of the <see cref="MemoryPoolState"/>. /// The location of the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
public enum LocationType : uint public enum LocationType : uint
{ {
/// <summary> /// <summary>
/// <see cref="MemoryPoolState"/> located on the CPU side for user use. /// <see cref="MemoryPoolInfo"/> located on the CPU side for user use.
/// </summary> /// </summary>
Cpu, Cpu,
/// <summary> /// <summary>
/// <see cref="MemoryPoolState"/> located on the DSP side for system use. /// <see cref="MemoryPoolInfo"/> located on the DSP side for system use.
/// </summary> /// </summary>
Dsp, Dsp,
} }
/// <summary> /// <summary>
/// The CPU address associated to the <see cref="MemoryPoolState"/>. /// The CPU address associated to the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
public CpuAddress CpuAddress; public CpuAddress CpuAddress;
/// <summary> /// <summary>
/// The DSP address associated to the <see cref="MemoryPoolState"/>. /// The DSP address associated to the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
public DspAddress DspAddress; public DspAddress DspAddress;
/// <summary> /// <summary>
/// The size associated to the <see cref="MemoryPoolState"/>. /// The size associated to the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
public ulong Size; public ulong Size;
/// <summary> /// <summary>
/// The <see cref="LocationType"/> associated to the <see cref="MemoryPoolState"/>. /// The <see cref="LocationType"/> associated to the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
public LocationType Location; public LocationType Location;
/// <summary> /// <summary>
/// Set to true if the <see cref="MemoryPoolState"/> is used. /// Set to true if the <see cref="MemoryPoolInfo"/> is used.
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool IsUsed; public bool IsUsed;
public static unsafe MemoryPoolState* Null => (MemoryPoolState*)nint.Zero.ToPointer(); public static unsafe MemoryPoolInfo* Null => (MemoryPoolInfo*)nint.Zero.ToPointer();
/// <summary> /// <summary>
/// Create a new <see cref="MemoryPoolState"/> with the given <see cref="LocationType"/>. /// Create a new <see cref="MemoryPoolInfo"/> with the given <see cref="LocationType"/>.
/// </summary> /// </summary>
/// <param name="location">The location type to use.</param> /// <param name="location">The location type to use.</param>
/// <returns>A new <see cref="MemoryPoolState"/> with the given <see cref="LocationType"/>.</returns> /// <returns>A new <see cref="MemoryPoolInfo"/> with the given <see cref="LocationType"/>.</returns>
public static MemoryPoolState Create(LocationType location) public static MemoryPoolInfo Create(LocationType location)
{ {
return new MemoryPoolState return new MemoryPoolInfo
{ {
CpuAddress = 0, CpuAddress = 0,
DspAddress = 0, DspAddress = 0,
@@ -73,7 +73,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Set the <see cref="CpuAddress"/> and size of the <see cref="MemoryPoolState"/>. /// Set the <see cref="CpuAddress"/> and size of the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
/// <param name="cpuAddress">The <see cref="CpuAddress"/>.</param> /// <param name="cpuAddress">The <see cref="CpuAddress"/>.</param>
/// <param name="size">The size.</param> /// <param name="size">The size.</param>
@@ -84,11 +84,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Check if the given <see cref="CpuAddress"/> and size is contains in the <see cref="MemoryPoolState"/>. /// Check if the given <see cref="CpuAddress"/> and size is contains in the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
/// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param> /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param>
/// <param name="size">The size.</param> /// <param name="size">The size.</param>
/// <returns>True if the <see cref="CpuAddress"/> is contained inside the <see cref="MemoryPoolState"/>.</returns> /// <returns>True if the <see cref="CpuAddress"/> is contained inside the <see cref="MemoryPoolInfo"/>.</returns>
public readonly bool Contains(CpuAddress targetCpuAddress, ulong size) public readonly bool Contains(CpuAddress targetCpuAddress, ulong size)
{ {
if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress) if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress)
@@ -118,9 +118,9 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Is the <see cref="MemoryPoolState"/> mapped on the DSP? /// Is the <see cref="MemoryPoolInfo"/> mapped on the DSP?
/// </summary> /// </summary>
/// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped on the DSP.</returns> /// <returns>Returns true if the <see cref="MemoryPoolInfo"/> is mapped on the DSP.</returns>
public readonly bool IsMapped() public readonly bool IsMapped()
{ {
return DspAddress != 0; return DspAddress != 0;

View File

@@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
const uint CurrentProcessPseudoHandle = 0xFFFF8001; const uint CurrentProcessPseudoHandle = 0xFFFF8001;
/// <summary> /// <summary>
/// The result of <see cref="Update(ref MemoryPoolState, ref MemoryPoolInParameter, ref MemoryPoolOutStatus)"/>. /// The result of <see cref="Update(ref MemoryPoolInfo, ref MemoryPoolInParameter, ref MemoryPoolOutStatus)"/>.
/// </summary> /// </summary>
public enum UpdateResult : uint public enum UpdateResult : uint
{ {
@@ -49,9 +49,9 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
private readonly uint _processHandle; private readonly uint _processHandle;
/// <summary> /// <summary>
/// The <see cref="Memory{MemoryPoolState}"/> that will be manipulated. /// The <see cref="Memory{MemoryPoolInfo}"/> that will be manipulated.
/// </summary> /// </summary>
private readonly Memory<MemoryPoolState> _memoryPools; private readonly Memory<MemoryPoolInfo> _memoryPools;
/// <summary> /// <summary>
/// If set to true, this will try to force map memory pool even if their state are considered invalid. /// If set to true, this will try to force map memory pool even if their state are considered invalid.
@@ -67,7 +67,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
{ {
_processHandle = processHandle; _processHandle = processHandle;
_isForceMapEnabled = isForceMapEnabled; _isForceMapEnabled = isForceMapEnabled;
_memoryPools = Memory<MemoryPoolState>.Empty; _memoryPools = Memory<MemoryPoolInfo>.Empty;
} }
/// <summary> /// <summary>
@@ -76,7 +76,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
/// <param name="processHandle">The handle of the process owning the CPU memory manipulated.</param> /// <param name="processHandle">The handle of the process owning the CPU memory manipulated.</param>
/// <param name="memoryPool">The user memory pools.</param> /// <param name="memoryPool">The user memory pools.</param>
/// <param name="isForceMapEnabled">If set to true, this will try to force map memory pool even if their state are considered invalid.</param> /// <param name="isForceMapEnabled">If set to true, this will try to force map memory pool even if their state are considered invalid.</param>
public PoolMapper(uint processHandle, Memory<MemoryPoolState> memoryPool, bool isForceMapEnabled) public PoolMapper(uint processHandle, Memory<MemoryPoolInfo> memoryPool, bool isForceMapEnabled)
{ {
_processHandle = processHandle; _processHandle = processHandle;
_memoryPools = memoryPool; _memoryPools = memoryPool;
@@ -84,15 +84,15 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Initialize the <see cref="MemoryPoolState"/> for system use. /// Initialize the <see cref="MemoryPoolInfo"/> for system use.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="MemoryPoolState"/> for system use.</param> /// <param name="memoryPool">The <see cref="MemoryPoolInfo"/> for system use.</param>
/// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param> /// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param>
/// <param name="size">The size to assign.</param> /// <param name="size">The size to assign.</param>
/// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns> /// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns>
public bool InitializeSystemPool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) public bool InitializeSystemPool(ref MemoryPoolInfo memoryPool, CpuAddress cpuAddress, ulong size)
{ {
if (memoryPool.Location != MemoryPoolState.LocationType.Dsp) if (memoryPool.Location != MemoryPoolInfo.LocationType.Dsp)
{ {
return false; return false;
} }
@@ -101,13 +101,13 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Initialize the <see cref="MemoryPoolState"/>. /// Initialize the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="MemoryPoolState"/>.</param> /// <param name="memoryPool">The <see cref="MemoryPoolInfo"/>.</param>
/// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param> /// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param>
/// <param name="size">The size to assign.</param> /// <param name="size">The size to assign.</param>
/// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns> /// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns>
public bool InitializePool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) public bool InitializePool(ref MemoryPoolInfo memoryPool, CpuAddress cpuAddress, ulong size)
{ {
memoryPool.SetCpuAddress(cpuAddress, size); memoryPool.SetCpuAddress(cpuAddress, size);
@@ -115,18 +115,18 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Get the process handle associated to the <see cref="MemoryPoolState"/>. /// Get the process handle associated to the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="MemoryPoolState"/>.</param> /// <param name="memoryPool">The <see cref="MemoryPoolInfo"/>.</param>
/// <returns>Returns the process handle associated to the <see cref="MemoryPoolState"/>.</returns> /// <returns>Returns the process handle associated to the <see cref="MemoryPoolInfo"/>.</returns>
public uint GetProcessHandle(ref MemoryPoolState memoryPool) public uint GetProcessHandle(ref MemoryPoolInfo memoryPool)
{ {
if (memoryPool.Location == MemoryPoolState.LocationType.Cpu) if (memoryPool.Location == MemoryPoolInfo.LocationType.Cpu)
{ {
return CurrentProcessPseudoHandle; return CurrentProcessPseudoHandle;
} }
if (memoryPool.Location == MemoryPoolState.LocationType.Dsp) if (memoryPool.Location == MemoryPoolInfo.LocationType.Dsp)
{ {
return _processHandle; return _processHandle;
} }
@@ -135,11 +135,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Map the <see cref="MemoryPoolState"/> on the <see cref="Dsp.AudioProcessor"/>. /// Map the <see cref="MemoryPoolInfo"/> on the <see cref="Dsp.AudioProcessor"/>.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="MemoryPoolState"/> to map.</param> /// <param name="memoryPool">The <see cref="MemoryPoolInfo"/> to map.</param>
/// <returns>Returns the DSP address mapped.</returns> /// <returns>Returns the DSP address mapped.</returns>
public DspAddress Map(ref MemoryPoolState memoryPool) public DspAddress Map(ref MemoryPoolInfo memoryPool)
{ {
DspAddress result = AudioProcessorMemoryManager.Map(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size); DspAddress result = AudioProcessorMemoryManager.Map(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size);
@@ -152,11 +152,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Unmap the <see cref="MemoryPoolState"/> from the <see cref="Dsp.AudioProcessor"/>. /// Unmap the <see cref="MemoryPoolInfo"/> from the <see cref="Dsp.AudioProcessor"/>.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="MemoryPoolState"/> to unmap.</param> /// <param name="memoryPool">The <see cref="MemoryPoolInfo"/> to unmap.</param>
/// <returns>Returns true if unmapped.</returns> /// <returns>Returns true if unmapped.</returns>
public bool Unmap(ref MemoryPoolState memoryPool) public bool Unmap(ref MemoryPoolInfo memoryPool)
{ {
if (memoryPool.IsUsed) if (memoryPool.IsUsed)
{ {
@@ -172,12 +172,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Find a <see cref="MemoryPoolState"/> associated to the region given. /// Find a <see cref="MemoryPoolInfo"/> associated to the region given.
/// </summary> /// </summary>
/// <param name="cpuAddress">The region <see cref="CpuAddress"/>.</param> /// <param name="cpuAddress">The region <see cref="CpuAddress"/>.</param>
/// <param name="size">The region size.</param> /// <param name="size">The region size.</param>
/// <returns>Returns the <see cref="MemoryPoolState"/> found or <see cref="Memory{MemoryPoolState}.Empty"/> if not found.</returns> /// <returns>Returns the <see cref="MemoryPoolInfo"/> found or <see cref="Memory{MemoryPoolInfo}.Empty"/> if not found.</returns>
private Span<MemoryPoolState> FindMemoryPool(CpuAddress cpuAddress, ulong size) private Span<MemoryPoolInfo> FindMemoryPool(CpuAddress cpuAddress, ulong size)
{ {
if (!_memoryPools.IsEmpty && _memoryPools.Length > 0) if (!_memoryPools.IsEmpty && _memoryPools.Length > 0)
{ {
@@ -190,7 +190,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
} }
return Span<MemoryPoolState>.Empty; return Span<MemoryPoolInfo>.Empty;
} }
/// <summary> /// <summary>
@@ -201,7 +201,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
{ {
if (_isForceMapEnabled) if (_isForceMapEnabled)
{ {
Span<MemoryPoolState> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); Span<MemoryPoolInfo> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size);
if (!memoryPool.IsEmpty) if (!memoryPool.IsEmpty)
{ {
@@ -243,13 +243,13 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Update a <see cref="MemoryPoolState"/> using user parameters. /// Update a <see cref="MemoryPoolInfo"/> using user parameters.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="MemoryPoolState"/> to update.</param> /// <param name="memoryPool">The <see cref="MemoryPoolInfo"/> to update.</param>
/// <param name="inParameter">Input user parameter.</param> /// <param name="inParameter">Input user parameter.</param>
/// <param name="outStatus">Output user parameter.</param> /// <param name="outStatus">Output user parameter.</param>
/// <returns>Returns the <see cref="UpdateResult"/> of the operations performed.</returns> /// <returns>Returns the <see cref="UpdateResult"/> of the operations performed.</returns>
public UpdateResult Update(ref MemoryPoolState memoryPool, in MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus) public UpdateResult Update(ref MemoryPoolInfo memoryPool, in MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus)
{ {
MemoryPoolUserState inputState = inParameter.State; MemoryPoolUserState inputState = inParameter.State;
@@ -321,7 +321,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
if (_memoryPools.Length > 0) if (_memoryPools.Length > 0)
{ {
Span<MemoryPoolState> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); Span<MemoryPoolInfo> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size);
if (!memoryPool.IsEmpty) if (!memoryPool.IsEmpty)
{ {
@@ -343,7 +343,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
{ {
unsafe unsafe
{ {
addressInfo.SetupMemoryPool(MemoryPoolState.Null); addressInfo.SetupMemoryPool(MemoryPoolInfo.Null);
} }
} }
@@ -351,12 +351,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
} }
/// <summary> /// <summary>
/// Remove the usage flag from all the <see cref="MemoryPoolState"/>. /// Remove the usage flag from all the <see cref="MemoryPoolInfo"/>.
/// </summary> /// </summary>
/// <param name="memoryPool">The <see cref="Memory{MemoryPoolState}"/> to reset.</param> /// <param name="memoryPool">The <see cref="Memory{MemoryPoolInfo}"/> to reset.</param>
public static void ClearUsageState(Memory<MemoryPoolState> memoryPool) public static void ClearUsageState(Memory<MemoryPoolInfo> memoryPool)
{ {
foreach (ref MemoryPoolState info in memoryPool.Span) foreach (ref MemoryPoolInfo info in memoryPool.Span)
{ {
info.IsUsed = false; info.IsUsed = false;
} }

View File

@@ -17,12 +17,12 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
private uint _mixesCount; private uint _mixesCount;
/// <summary> /// <summary>
/// Storage for <see cref="MixState"/>. /// Storage for <see cref="MixInfo"/>.
/// </summary> /// </summary>
private Memory<MixState> _mixes; private Memory<MixInfo> _mixes;
/// <summary> /// <summary>
/// Storage of the sorted indices to <see cref="MixState"/>. /// Storage of the sorted indices to <see cref="MixInfo"/>.
/// </summary> /// </summary>
private Memory<int> _sortedMixes; private Memory<int> _sortedMixes;
@@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
/// Initialize the <see cref="MixContext"/>. /// Initialize the <see cref="MixContext"/>.
/// </summary> /// </summary>
/// <param name="sortedMixes">The storage for sorted indices.</param> /// <param name="sortedMixes">The storage for sorted indices.</param>
/// <param name="mixes">The storage of <see cref="MixState"/>.</param> /// <param name="mixes">The storage of <see cref="MixInfo"/>.</param>
/// <param name="nodeStatesWorkBuffer">The storage used for the <see cref="NodeStates"/>.</param> /// <param name="nodeStatesWorkBuffer">The storage used for the <see cref="NodeStates"/>.</param>
/// <param name="edgeMatrixWorkBuffer">The storage used for the <see cref="EdgeMatrix"/>.</param> /// <param name="edgeMatrixWorkBuffer">The storage used for the <see cref="EdgeMatrix"/>.</param>
public void Initialize(Memory<int> sortedMixes, Memory<MixState> mixes, Memory<byte> nodeStatesWorkBuffer, Memory<byte> edgeMatrixWorkBuffer) public void Initialize(Memory<int> sortedMixes, Memory<MixInfo> mixes, Memory<byte> nodeStatesWorkBuffer, Memory<byte> edgeMatrixWorkBuffer)
{ {
_mixesCount = (uint)mixes.Length; _mixesCount = (uint)mixes.Length;
_mixes = mixes; _mixes = mixes;
@@ -82,30 +82,30 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
} }
/// <summary> /// <summary>
/// Get a reference to the final <see cref="MixState"/>. /// Get a reference to the final <see cref="MixInfo"/>.
/// </summary> /// </summary>
/// <returns>A reference to the final <see cref="MixState"/>.</returns> /// <returns>A reference to the final <see cref="MixInfo"/>.</returns>
public ref MixState GetFinalState() public ref MixInfo GetFinalState()
{ {
return ref GetState(Constants.FinalMixId); return ref GetState(Constants.FinalMixId);
} }
/// <summary> /// <summary>
/// Get a reference to a <see cref="MixState"/> at the given <paramref name="id"/>. /// Get a reference to a <see cref="MixInfo"/> at the given <paramref name="id"/>.
/// </summary> /// </summary>
/// <param name="id">The index to use.</param> /// <param name="id">The index to use.</param>
/// <returns>A reference to a <see cref="MixState"/> at the given <paramref name="id"/>.</returns> /// <returns>A reference to a <see cref="MixInfo"/> at the given <paramref name="id"/>.</returns>
public ref MixState GetState(int id) public ref MixInfo GetState(int id)
{ {
return ref SpanIOHelper.GetFromMemory(_mixes, id, _mixesCount); return ref SpanIOHelper.GetFromMemory(_mixes, id, _mixesCount);
} }
/// <summary> /// <summary>
/// Get a reference to a <see cref="MixState"/> at the given <paramref name="id"/> of the sorted mix info. /// Get a reference to a <see cref="MixInfo"/> at the given <paramref name="id"/> of the sorted mix info.
/// </summary> /// </summary>
/// <param name="id">The index to use.</param> /// <param name="id">The index to use.</param>
/// <returns>A reference to a <see cref="MixState"/> at the given <paramref name="id"/>.</returns> /// <returns>A reference to a <see cref="MixInfo"/> at the given <paramref name="id"/>.</returns>
public ref MixState GetSortedState(int id) public ref MixInfo GetSortedState(int id)
{ {
Debug.Assert(id >= 0 && id < _mixesCount); Debug.Assert(id >= 0 && id < _mixesCount);
@@ -122,18 +122,18 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
} }
/// <summary> /// <summary>
/// Update the internal distance from the final mix value of every <see cref="MixState"/>. /// Update the internal distance from the final mix value of every <see cref="MixInfo"/>.
/// </summary> /// </summary>
private void UpdateDistancesFromFinalMix() private void UpdateDistancesFromFinalMix()
{ {
foreach (ref MixState mix in _mixes.Span) foreach (ref MixInfo mix in _mixes.Span)
{ {
mix.ClearDistanceFromFinalMix(); mix.ClearDistanceFromFinalMix();
} }
for (int i = 0; i < GetCount(); i++) for (int i = 0; i < GetCount(); i++)
{ {
ref MixState mix = ref GetState(i); ref MixInfo mix = ref GetState(i);
SetSortedState(i, i); SetSortedState(i, i);
@@ -149,13 +149,13 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
{ {
if (mixId == Constants.UnusedMixId) if (mixId == Constants.UnusedMixId)
{ {
distance = MixState.InvalidDistanceFromFinalMix; distance = MixInfo.InvalidDistanceFromFinalMix;
break; break;
} }
ref MixState distanceMix = ref GetState(mixId); ref MixInfo distanceMix = ref GetState(mixId);
if (distanceMix.DistanceFromFinalMix != MixState.InvalidDistanceFromFinalMix) if (distanceMix.DistanceFromFinalMix != MixInfo.InvalidDistanceFromFinalMix)
{ {
distance = distanceMix.DistanceFromFinalMix + 1; distance = distanceMix.DistanceFromFinalMix + 1;
break; break;
@@ -171,12 +171,12 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
if (distance > GetCount()) if (distance > GetCount())
{ {
distance = MixState.InvalidDistanceFromFinalMix; distance = MixInfo.InvalidDistanceFromFinalMix;
} }
} }
else else
{ {
distance = MixState.InvalidDistanceFromFinalMix; distance = MixInfo.InvalidDistanceFromFinalMix;
} }
mix.DistanceFromFinalMix = distance; mix.DistanceFromFinalMix = distance;
@@ -185,13 +185,13 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
} }
/// <summary> /// <summary>
/// Update the internal mix buffer offset of all <see cref="MixState"/>. /// Update the internal mix buffer offset of all <see cref="MixInfo"/>.
/// </summary> /// </summary>
private void UpdateMixBufferOffset() private void UpdateMixBufferOffset()
{ {
uint offset = 0; uint offset = 0;
foreach (ref MixState mix in _mixes.Span) foreach (ref MixInfo mix in _mixes.Span)
{ {
mix.BufferOffset = offset; mix.BufferOffset = offset;
@@ -210,10 +210,10 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
Array.Sort(sortedMixesTemp, (a, b) => Array.Sort(sortedMixesTemp, (a, b) =>
{ {
ref MixState stateA = ref GetState(a); ref MixInfo infoA = ref GetState(a);
ref MixState stateB = ref GetState(b); ref MixInfo infoB = ref GetState(b);
return stateB.DistanceFromFinalMix.CompareTo(stateA.DistanceFromFinalMix); return infoB.DistanceFromFinalMix.CompareTo(infoA.DistanceFromFinalMix);
}); });
sortedMixesTemp.AsSpan().CopyTo(_sortedMixes.Span); sortedMixesTemp.AsSpan().CopyTo(_sortedMixes.Span);

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
/// Server state for a mix. /// Server state for a mix.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x940, Pack = Alignment)] [StructLayout(LayoutKind.Sequential, Size = 0x940, Pack = Alignment)]
public struct MixState public struct MixInfo
{ {
public const uint InvalidDistanceFromFinalMix = 0x80000000; public const uint InvalidDistanceFromFinalMix = 0x80000000;
@@ -136,11 +136,11 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
} }
/// <summary> /// <summary>
/// Create a new <see cref="MixState"/> /// Create a new <see cref="MixInfo"/>
/// </summary> /// </summary>
/// <param name="effectProcessingOrderArray"></param> /// <param name="effectProcessingOrderArray"></param>
/// <param name="behaviourContext"></param> /// <param name="behaviourInfo"></param>
public MixState(Memory<int> effectProcessingOrderArray, ref BehaviourContext behaviourContext) : this() public MixInfo(Memory<int> effectProcessingOrderArray, ref BehaviourInfo behaviourInfo) : this()
{ {
MixId = UnusedMixId; MixId = UnusedMixId;
@@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
EffectProcessingOrderArrayMaxCount = (uint)effectProcessingOrderArray.Length; EffectProcessingOrderArrayMaxCount = (uint)effectProcessingOrderArray.Length;
IsLongSizePreDelaySupported = behaviourContext.IsLongSizePreDelaySupported(); IsLongSizePreDelaySupported = behaviourInfo.IsLongSizePreDelaySupported();
ClearEffectProcessingOrder(); ClearEffectProcessingOrder();
} }
@@ -257,9 +257,9 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
/// <param name="parameter">The input parameter of the mix.</param> /// <param name="parameter">The input parameter of the mix.</param>
/// <param name="effectContext">The effect context.</param> /// <param name="effectContext">The effect context.</param>
/// <param name="splitterContext">The splitter context.</param> /// <param name="splitterContext">The splitter context.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
/// <returns>Return true if the mix was changed.</returns> /// <returns>Return true if the mix was changed.</returns>
public bool Update(EdgeMatrix edgeMatrix, in MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext) public bool Update(EdgeMatrix edgeMatrix, in MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourInfo behaviourInfo)
{ {
bool isDirty; bool isDirty;
@@ -271,7 +271,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
NodeId = parameter.NodeId; NodeId = parameter.NodeId;
parameter.MixBufferVolume.CopyTo(MixBufferVolume); parameter.MixBufferVolume.CopyTo(MixBufferVolume);
if (behaviourContext.IsSplitterSupported()) if (behaviourInfo.IsSplitterSupported())
{ {
isDirty = UpdateConnection(edgeMatrix, in parameter, ref splitterContext); isDirty = UpdateConnection(edgeMatrix, in parameter, ref splitterContext);
} }
@@ -279,10 +279,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
{ {
isDirty = DestinationMixId != parameter.DestinationMixId; isDirty = DestinationMixId != parameter.DestinationMixId;
if (DestinationMixId != parameter.DestinationMixId) DestinationMixId = parameter.DestinationMixId;
{
DestinationMixId = parameter.DestinationMixId;
}
DestinationSplitterId = UnusedSplitterId; DestinationSplitterId = UnusedSplitterId;
} }

View File

@@ -10,11 +10,11 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
/// Get the required size for a single performance frame. /// Get the required size for a single performance frame.
/// </summary> /// </summary>
/// <param name="parameter">The audio renderer configuration.</param> /// <param name="parameter">The audio renderer configuration.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
/// <returns>The required size for a single performance frame.</returns> /// <returns>The required size for a single performance frame.</returns>
public static ulong GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter, ref BehaviourContext behaviourContext) public static ulong GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter, ref BehaviourInfo behaviourInfo)
{ {
uint version = behaviourContext.GetPerformanceMetricsDataFormat(); uint version = behaviourInfo.GetPerformanceMetricsDataFormat();
if (version == 2) if (version == 2)
{ {
@@ -81,11 +81,11 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
/// </summary> /// </summary>
/// <param name="performanceBuffer">The backing memory available for use by the manager.</param> /// <param name="performanceBuffer">The backing memory available for use by the manager.</param>
/// <param name="parameter">The audio renderer configuration.</param> /// <param name="parameter">The audio renderer configuration.</param>
/// <param name="behaviourContext">The behaviour context;</param> /// <param name="behaviourInfo">The behaviour context;</param>
/// <returns>A new <see cref="PerformanceManager"/>.</returns> /// <returns>A new <see cref="PerformanceManager"/>.</returns>
public static PerformanceManager Create(Memory<byte> performanceBuffer, ref AudioRendererConfiguration parameter, BehaviourContext behaviourContext) public static PerformanceManager Create(Memory<byte> performanceBuffer, ref AudioRendererConfiguration parameter, BehaviourInfo behaviourInfo)
{ {
uint version = behaviourContext.GetPerformanceMetricsDataFormat(); uint version = behaviourInfo.GetPerformanceMetricsDataFormat();
return version switch return version switch
{ {

View File

@@ -1,3 +1,4 @@
using Ryujinx.Audio.Renderer.Server.Mix;
using Ryujinx.Audio.Renderer.Server.Upsampler; using Ryujinx.Audio.Renderer.Server.Upsampler;
using System; using System;
@@ -19,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// The target channel count for sink. /// The target channel count for sink.
/// </summary> /// </summary>
/// <remarks>See <see cref="CommandGenerator.GenerateDevice(Sink.DeviceSink, ref Mix.MixState)"/> for usage.</remarks> /// <remarks>See <see cref="CommandGenerator.GenerateDevice(Sink.DeviceSink, ref MixInfo)"/> for usage.</remarks>
public uint ChannelCount; public uint ChannelCount;
/// <summary> /// <summary>
@@ -28,12 +29,12 @@ namespace Ryujinx.Audio.Renderer.Server
public uint MixBufferCount; public uint MixBufferCount;
/// <summary> /// <summary>
/// Instance of the <see cref="BehaviourContext"/> used to derive bug fixes and features of the current audio renderer revision. /// Instance of the <see cref="BehaviourInfo"/> used to derive bug fixes and features of the current audio renderer revision.
/// </summary> /// </summary>
public BehaviourContext BehaviourContext; public BehaviourInfo BehaviourInfo;
/// <summary> /// <summary>
/// Instance of the <see cref="UpsamplerManager"/> used for upsampling (see <see cref="UpsamplerState"/>) /// Instance of the <see cref="UpsamplerManager"/> used for upsampling (see <see cref="UpsamplerInfo"/>)
/// </summary> /// </summary>
public UpsamplerManager UpsamplerManager; public UpsamplerManager UpsamplerManager;

View File

@@ -28,7 +28,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
/// The upsampler instance used by this sink. /// The upsampler instance used by this sink.
/// </summary> /// </summary>
/// <remarks>Null if no upsampling is needed.</remarks> /// <remarks>Null if no upsampling is needed.</remarks>
public UpsamplerState UpsamplerState; public UpsamplerInfo UpsamplerInfo;
/// <summary> /// <summary>
/// Create a new <see cref="DeviceSink"/>. /// Create a new <see cref="DeviceSink"/>.
@@ -40,9 +40,9 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
public override void CleanUp() public override void CleanUp()
{ {
UpsamplerState?.Release(); UpsamplerInfo?.Release();
UpsamplerState = null; UpsamplerInfo = null;
base.CleanUp(); base.CleanUp();
} }

View File

@@ -55,22 +55,27 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use. /// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use.
/// </summary> /// </summary>
public bool IsSplitterPrevVolumeResetSupported { get; private set; } public bool IsSplitterPrevVolumeResetSupported { get; private set; }
/// <summary>
/// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use.
/// </summary>
public bool IsBiquadFilterParameterFloatSupported { get; private set; }
/// <summary> /// <summary>
/// Initialize <see cref="SplitterContext"/>. /// Initialize <see cref="SplitterContext"/>.
/// </summary> /// </summary>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
/// <param name="parameter">The audio renderer configuration.</param> /// <param name="parameter">The audio renderer configuration.</param>
/// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param> /// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param>
/// <param name="splitterBqfStates">Memory to store the biquad filtering state for splitters during processing.</param> /// <param name="splitterBqfStates">Memory to store the biquad filtering state for splitters during processing.</param>
/// <returns>Return true if the initialization was successful.</returns> /// <returns>Return true if the initialization was successful.</returns>
public bool Initialize( public bool Initialize(
ref BehaviourContext behaviourContext, ref BehaviourInfo behaviourInfo,
ref AudioRendererConfiguration parameter, ref AudioRendererConfiguration parameter,
WorkBufferAllocator workBufferAllocator, WorkBufferAllocator workBufferAllocator,
Memory<BiquadFilterState> splitterBqfStates) Memory<BiquadFilterState> splitterBqfStates)
{ {
if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0) if (!behaviourInfo.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0)
{ {
Setup(Memory<SplitterState>.Empty, Memory<SplitterDestinationVersion1>.Empty, Memory<SplitterDestinationVersion2>.Empty, false); Setup(Memory<SplitterState>.Empty, Memory<SplitterDestinationVersion1>.Empty, Memory<SplitterDestinationVersion2>.Empty, false);
@@ -94,7 +99,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
Memory<SplitterDestinationVersion1> splitterDestinationsV1 = Memory<SplitterDestinationVersion1>.Empty; Memory<SplitterDestinationVersion1> splitterDestinationsV1 = Memory<SplitterDestinationVersion1>.Empty;
Memory<SplitterDestinationVersion2> splitterDestinationsV2 = Memory<SplitterDestinationVersion2>.Empty; Memory<SplitterDestinationVersion2> splitterDestinationsV2 = Memory<SplitterDestinationVersion2>.Empty;
if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) if (!behaviourInfo.IsBiquadFilterParameterForSplitterEnabled())
{ {
Version = 1; Version = 1;
@@ -144,11 +149,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
} }
} }
IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported(); IsSplitterPrevVolumeResetSupported = behaviourInfo.IsSplitterPrevVolumeResetSupported();
IsBiquadFilterParameterFloatSupported = behaviourInfo.IsBiquadFilterParameterFloatSupported();
SplitterState.InitializeSplitters(splitters.Span); SplitterState.InitializeSplitters(splitters.Span);
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed()); Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourInfo.IsSplitterBugFixed());
return true; return true;
} }
@@ -157,16 +163,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// Get the work buffer size while adding the size needed for splitter to operate. /// Get the work buffer size while adding the size needed for splitter to operate.
/// </summary> /// </summary>
/// <param name="size">The current size.</param> /// <param name="size">The current size.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
/// <param name="parameter">The renderer configuration.</param> /// <param name="parameter">The renderer configuration.</param>
/// <returns>Return the new size taking splitter into account.</returns> /// <returns>Return the new size taking splitter into account.</returns>
public static ulong GetWorkBufferSize(ulong size, ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter) public static ulong GetWorkBufferSize(ulong size, ref BehaviourInfo behaviourInfo, ref AudioRendererConfiguration parameter)
{ {
if (behaviourContext.IsSplitterSupported()) if (behaviourInfo.IsSplitterSupported())
{ {
size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment); size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment);
if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) if (behaviourInfo.IsBiquadFilterParameterForSplitterEnabled())
{ {
size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion2>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment); size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion2>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment);
} }
@@ -175,12 +181,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion1>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment); size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion1>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment);
} }
if (behaviourContext.IsSplitterBugFixed()) if (behaviourInfo.IsSplitterBugFixed())
{ {
size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.SplitterDestinationCount, 0x10); size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.SplitterDestinationCount, 0x10);
} }
return size;
} }
return size; return size;
@@ -227,7 +231,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
return 0; return 0;
} }
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; int length;
if (_splitterDestinationsV2.IsEmpty)
{
length = _splitterDestinationsV1.Length;
}
else
{
length = _splitterDestinationsV2.Length;
}
return length / _splitters.Length; return length / _splitters.Length;
} }
@@ -278,8 +291,17 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
if (parameter.IsMagicValid()) if (parameter.IsMagicValid())
{ {
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; int length;
if (_splitterDestinationsV2.IsEmpty)
{
length = _splitterDestinationsV1.Length;
}
else
{
length = _splitterDestinationsV2.Length;
}
if (parameter.Id >= 0 && parameter.Id < length) if (parameter.Id >= 0 && parameter.Id < length)
{ {
SplitterDestination destination = GetDestination(parameter.Id); SplitterDestination destination = GetDestination(parameter.Id);
@@ -315,9 +337,19 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
} }
else if (Version == 2) else if (Version == 2)
{ {
if (!UpdateData<SplitterDestinationInParameterVersion2>(ref input)) if (IsBiquadFilterParameterFloatSupported)
{ {
break; if (!UpdateData<SplitterDestinationInParameterVersion2b>(ref input))
{
break;
}
}
else
{
if (!UpdateData<SplitterDestinationInParameterVersion2a>(ref input))
{
break;
}
} }
} }
else else
@@ -381,10 +413,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{ {
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length)); return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length));
} }
else
{ return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length));
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length));
}
} }
/// <summary> /// <summary>

View File

@@ -31,15 +31,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{ {
return 0; return 0;
} }
else
{ return _v1.Id;
return _v1.Id;
}
}
else
{
return _v2.Id;
} }
return _v2.Id;
} }
} }
@@ -56,15 +52,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{ {
return 0; return 0;
} }
else
{ return _v1.DestinationId;
return _v1.DestinationId;
}
}
else
{
return _v2.DestinationId;
} }
return _v2.DestinationId;
} }
} }
@@ -82,15 +74,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{ {
return Span<float>.Empty; return Span<float>.Empty;
} }
else
{ return _v1.MixBufferVolume;
return _v1.MixBufferVolume;
}
}
else
{
return _v2.MixBufferVolume;
} }
return _v2.MixBufferVolume;
} }
} }
@@ -108,15 +96,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{ {
return Span<float>.Empty; return Span<float>.Empty;
} }
else
{ return _v1.PreviousMixBufferVolume;
return _v1.PreviousMixBufferVolume;
}
}
else
{
return _v2.PreviousMixBufferVolume;
} }
return _v2.PreviousMixBufferVolume;
} }
} }
@@ -135,15 +119,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{ {
return new SplitterDestination(); return new SplitterDestination();
} }
else
{ return new SplitterDestination(ref _v1.Next);
return new SplitterDestination(ref _v1.Next);
}
}
else
{
return new SplitterDestination(ref _v2.Next);
} }
return new SplitterDestination(ref _v2.Next);
} }
} }
} }
@@ -169,6 +149,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
_v2 = ref v2; _v2 = ref v2;
} }
/// <summary> /// <summary>
/// Creates a new splitter destination wrapper for the splitter destination data. /// Creates a new splitter destination wrapper for the splitter destination data.
/// </summary> /// </summary>
@@ -233,7 +214,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>True if the splitter destination is used and has a destination.</returns> /// <returns>True if the splitter destination is used and has a destination.</returns>
public readonly bool IsConfigured() public readonly bool IsConfigured()
{ {
return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured(); if (Unsafe.IsNullRef(ref _v2))
{
return _v1.IsConfigured();
}
return _v2.IsConfigured();
} }
/// <summary> /// <summary>
@@ -243,7 +229,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>The volume for the given destination.</returns> /// <returns>The volume for the given destination.</returns>
public float GetMixVolume(int destinationIndex) public float GetMixVolume(int destinationIndex)
{ {
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex); if (Unsafe.IsNullRef(ref _v2))
{
return _v1.GetMixVolume(destinationIndex);
}
return _v2.GetMixVolume(destinationIndex);
} }
/// <summary> /// <summary>
@@ -253,7 +244,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>The volume for the given destination.</returns> /// <returns>The volume for the given destination.</returns>
public float GetMixVolumePrev(int destinationIndex) public float GetMixVolumePrev(int destinationIndex)
{ {
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex); if (Unsafe.IsNullRef(ref _v2))
{
return _v1.GetMixVolumePrev(destinationIndex);
}
return _v2.GetMixVolumePrev(destinationIndex);
} }
/// <summary> /// <summary>
@@ -280,13 +276,13 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
if (Unsafe.IsNullRef(ref _v2)) if (Unsafe.IsNullRef(ref _v2))
{ {
Debug.Assert(!Unsafe.IsNullRef(ref next._v1)); Debug.Assert(!Unsafe.IsNullRef(ref next._v1));
_v1.Link(ref next._v1); _v1.Link(ref next._v1);
} }
else else
{ {
Debug.Assert(!Unsafe.IsNullRef(ref next._v2)); Debug.Assert(!Unsafe.IsNullRef(ref next._v2));
_v2.Link(ref next._v2); _v2.Link(ref next._v2);
} }
} }
@@ -308,6 +304,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <summary> /// <summary>
/// Checks if any biquad filter is enabled. /// Checks if any biquad filter is enabled.
/// Virtual function at function table + 0x8.
/// </summary> /// </summary>
/// <returns>True if any biquad filter is enabled.</returns> /// <returns>True if any biquad filter is enabled.</returns>
public bool IsBiquadFilterEnabled() public bool IsBiquadFilterEnabled()
@@ -326,13 +323,14 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <summary> /// <summary>
/// Gets the biquad filter parameters. /// Gets the biquad filter parameters.
/// /// Virtual function at function table + 0x10.
/// </summary> /// </summary>
/// <param name="index">Biquad filter index (0 or 1).</param> /// <param name="index">Biquad filter index (0 or 1).</param>
/// <returns>Biquad filter parameters.</returns> /// <returns>Biquad filter parameters.</returns>
public ref BiquadFilterParameter GetBiquadFilterParameter(int index) public ref BiquadFilterParameter2 GetBiquadFilterParameter(int index)
{ {
Debug.Assert(!Unsafe.IsNullRef(ref _v2)); Debug.Assert(!Unsafe.IsNullRef(ref _v2));
return ref _v2.GetBiquadFilterParameter(index); return ref _v2.GetBiquadFilterParameter(index);
} }

View File

@@ -1,3 +1,4 @@
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -11,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <summary> /// <summary>
/// Server state for a splitter destination (version 2). /// Server state for a splitter destination (version 2).
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x110, Pack = Alignment)] [StructLayout(LayoutKind.Sequential, Size = 0x128, Pack = Alignment)]
public struct SplitterDestinationVersion2 public struct SplitterDestinationVersion2
{ {
public const int Alignment = 0x10; public const int Alignment = 0x10;
@@ -78,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
} }
} }
private Array2<BiquadFilterParameter> _biquadFilters; private Array2<BiquadFilterParameter2> _biquadFilters;
private Array2<bool> _isPreviousBiquadFilterEnabled; private Array2<bool> _isPreviousBiquadFilterEnabled;
@@ -109,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
parameter.MixBufferVolume.CopyTo(MixBufferVolume); parameter.MixBufferVolume.CopyTo(MixBufferVolume);
_biquadFilters = parameter.BiquadFilters; _biquadFilters = parameter.BiquadFilters2;
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed; bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
if (resetPrevVolume) if (resetPrevVolume)
@@ -218,7 +219,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<BiquadFilterParameter2> biquadFiltersSpan = _biquadFilters.AsSpan();
return biquadFiltersSpan[0].Enable || biquadFiltersSpan[1].Enable;
} }
/// <summary> /// <summary>
@@ -235,7 +237,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary> /// </summary>
/// <param name="index">Biquad filter index (0 or 1).</param> /// <param name="index">Biquad filter index (0 or 1).</param>
/// <returns>Biquad filter parameters.</returns> /// <returns>Biquad filter parameters.</returns>
public ref BiquadFilterParameter GetBiquadFilterParameter(int index) public ref BiquadFilterParameter2 GetBiquadFilterParameter(int index)
{ {
return ref _biquadFilters[index]; return ref _biquadFilters[index];
} }

View File

@@ -27,39 +27,39 @@ namespace Ryujinx.Audio.Renderer.Server
private Memory<byte> _output; private Memory<byte> _output;
private readonly uint _processHandle; private readonly uint _processHandle;
private BehaviourContext _behaviourContext; private BehaviourInfo _behaviourInfo;
private readonly ref readonly UpdateDataHeader _inputHeader; private readonly ref readonly UpdateDataHeader _inputHeader;
private readonly Memory<UpdateDataHeader> _outputHeader; private readonly Memory<UpdateDataHeader> _outputHeader;
private readonly ref UpdateDataHeader OutputHeader => ref _outputHeader.Span[0]; private readonly ref UpdateDataHeader OutputHeader => ref _outputHeader.Span[0];
public StateUpdater(ReadOnlySequence<byte> input, Memory<byte> output, uint processHandle, BehaviourContext behaviourContext) public StateUpdater(ReadOnlySequence<byte> input, Memory<byte> output, uint processHandle, BehaviourInfo behaviourInfo)
{ {
_inputReader = new SequenceReader<byte>(input); _inputReader = new SequenceReader<byte>(input);
_output = output; _output = output;
_outputOrigin = _output; _outputOrigin = _output;
_processHandle = processHandle; _processHandle = processHandle;
_behaviourContext = behaviourContext; _behaviourInfo = behaviourInfo;
_inputHeader = ref _inputReader.GetRefOrRefToCopy<UpdateDataHeader>(out _); _inputHeader = ref _inputReader.GetRefOrRefToCopy<UpdateDataHeader>(out _);
_outputHeader = SpanMemoryManager<UpdateDataHeader>.Cast(_output[..Unsafe.SizeOf<UpdateDataHeader>()]); _outputHeader = SpanMemoryManager<UpdateDataHeader>.Cast(_output[..Unsafe.SizeOf<UpdateDataHeader>()]);
OutputHeader.Initialize(_behaviourContext.UserRevision); OutputHeader.Initialize(_behaviourInfo.UserRevision);
_output = _output[Unsafe.SizeOf<UpdateDataHeader>()..]; _output = _output[Unsafe.SizeOf<UpdateDataHeader>()..];
} }
public ResultCode UpdateBehaviourContext() public ResultCode UpdateBehaviourInfo()
{ {
ref readonly BehaviourParameter parameter = ref _inputReader.GetRefOrRefToCopy<BehaviourParameter>(out _); ref readonly BehaviourParameter parameter = ref _inputReader.GetRefOrRefToCopy<BehaviourParameter>(out _);
if (!BehaviourContext.CheckValidRevision(parameter.UserRevision) || parameter.UserRevision != _behaviourContext.UserRevision) if (!BehaviourInfo.CheckValidRevision(parameter.UserRevision) || parameter.UserRevision != _behaviourInfo.UserRevision)
{ {
return ResultCode.InvalidUpdateInfo; return ResultCode.InvalidUpdateInfo;
} }
_behaviourContext.ClearError(); _behaviourInfo.ClearError();
_behaviourContext.UpdateFlags(parameter.Flags); _behaviourInfo.UpdateFlags(parameter.Flags);
if (_inputHeader.BehaviourSize != Unsafe.SizeOf<BehaviourParameter>()) if (_inputHeader.BehaviourSize != Unsafe.SizeOf<BehaviourParameter>())
{ {
@@ -69,16 +69,16 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.Success; return ResultCode.Success;
} }
public ResultCode UpdateMemoryPools(Span<MemoryPoolState> memoryPools) public ResultCode UpdateMemoryPools(Span<MemoryPoolInfo> memoryPools)
{ {
PoolMapper mapper = new(_processHandle, _behaviourContext.IsMemoryPoolForceMappingEnabled()); PoolMapper mapper = new(_processHandle, _behaviourInfo.IsMemoryPoolForceMappingEnabled());
if (memoryPools.Length * Unsafe.SizeOf<MemoryPoolInParameter>() != _inputHeader.MemoryPoolsSize) if (memoryPools.Length * Unsafe.SizeOf<MemoryPoolInParameter>() != _inputHeader.MemoryPoolsSize)
{ {
return ResultCode.InvalidUpdateInfo; return ResultCode.InvalidUpdateInfo;
} }
foreach (ref MemoryPoolState memoryPool in memoryPools) foreach (ref MemoryPoolInfo memoryPool in memoryPools)
{ {
ref readonly MemoryPoolInParameter parameter = ref _inputReader.GetRefOrRefToCopy<MemoryPoolInParameter>(out _); ref readonly MemoryPoolInParameter parameter = ref _inputReader.GetRefOrRefToCopy<MemoryPoolInParameter>(out _);
@@ -125,10 +125,10 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.Success; return ResultCode.Success;
} }
public ResultCode UpdateVoices(VoiceContext context, PoolMapper mapper) public ResultCode UpdateVoices2(VoiceContext context, PoolMapper mapper)
{ {
if (context.GetCount() * Unsafe.SizeOf<VoiceInParameter>() != _inputHeader.VoicesSize) if (context.GetCount() * Unsafe.SizeOf<VoiceInParameter2>() != _inputHeader.VoicesSize)
{ {
return ResultCode.InvalidUpdateInfo; return ResultCode.InvalidUpdateInfo;
} }
@@ -140,64 +140,153 @@ namespace Ryujinx.Audio.Renderer.Server
// First make everything not in use. // First make everything not in use.
for (int i = 0; i < context.GetCount(); i++) for (int i = 0; i < context.GetCount(); i++)
{ {
ref VoiceState state = ref context.GetState(i); ref VoiceInfo info = ref context.GetState(i);
state.InUse = false; info.InUse = false;
} }
Memory<VoiceUpdateState>[] voiceUpdateStatesArray = ArrayPool<Memory<VoiceUpdateState>>.Shared.Rent(Constants.VoiceChannelCountMax); Memory<VoiceState>[] voiceStatesArray = ArrayPool<Memory<VoiceState>>.Shared.Rent(Constants.VoiceChannelCountMax);
Span<Memory<VoiceUpdateState>> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax); Span<Memory<VoiceState>> voiceStates = voiceStatesArray.AsSpan(0, Constants.VoiceChannelCountMax);
// Start processing // Start processing
for (int i = 0; i < context.GetCount(); i++) for (int i = 0; i < context.GetCount(); i++)
{ {
ref readonly VoiceInParameter parameter = ref _inputReader.GetRefOrRefToCopy<VoiceInParameter>(out _); ref readonly VoiceInParameter2 parameter = ref _inputReader.GetRefOrRefToCopy<VoiceInParameter2>(out _);
voiceUpdateStates.Fill(Memory<VoiceUpdateState>.Empty); voiceStates.Fill(Memory<VoiceState>.Empty);
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0]; ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
if (parameter.InUse) if (parameter.InUse)
{ {
ref VoiceState currentVoiceState = ref context.GetState(i); ref VoiceInfo currentVoiceInfo = 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());
voiceUpdateStates[channelResourceIndex] = context.GetUpdateStateForCpu(channelId); voiceStates[channelResourceIndex] = context.GetUpdateStateForCpu(channelId);
} }
if (parameter.IsNew) if (parameter.IsNew)
{ {
currentVoiceState.Initialize(); currentVoiceInfo.Initialize();
} }
currentVoiceState.UpdateParameters(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourContext); currentVoiceInfo.UpdateParameters2(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourInfo);
if (updateParameterError.ErrorCode != ResultCode.Success) if (updateParameterError.ErrorCode != ResultCode.Success)
{ {
_behaviourContext.AppendError(ref updateParameterError); _behaviourInfo.AppendError(ref updateParameterError);
} }
currentVoiceState.UpdateWaveBuffers(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceUpdateStates, mapper, ref _behaviourContext); currentVoiceInfo.UpdateWaveBuffers2(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceStates, mapper, ref _behaviourInfo);
foreach (ref ErrorInfo errorInfo in waveBufferUpdateErrorInfos.AsSpan()) foreach (ref ErrorInfo errorInfo in waveBufferUpdateErrorInfos.AsSpan())
{ {
if (errorInfo.ErrorCode != ResultCode.Success) if (errorInfo.ErrorCode != ResultCode.Success)
{ {
_behaviourContext.AppendError(ref errorInfo); _behaviourInfo.AppendError(ref errorInfo);
} }
} }
currentVoiceState.WriteOutStatus(ref outStatus, in parameter, voiceUpdateStates); currentVoiceInfo.WriteOutStatus2(ref outStatus, in parameter, voiceStates);
} }
} }
ArrayPool<Memory<VoiceUpdateState>>.Shared.Return(voiceUpdateStatesArray); ArrayPool<Memory<VoiceState>>.Shared.Return(voiceStatesArray);
int currentOutputSize = _output.Length;
OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount());
OutputHeader.TotalSize += OutputHeader.VoicesSize;
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.VoicesSize);
_inputReader.SetConsumed(initialInputConsumed + _inputHeader.VoicesSize);
return ResultCode.Success;
}
public ResultCode UpdateVoices1(VoiceContext context, PoolMapper mapper)
{
if (context.GetCount() * Unsafe.SizeOf<VoiceInParameter1>() != _inputHeader.VoicesSize)
{
return ResultCode.InvalidUpdateInfo;
}
int initialOutputSize = _output.Length;
long initialInputConsumed = _inputReader.Consumed;
// First make everything not in use.
for (int i = 0; i < context.GetCount(); i++)
{
ref VoiceInfo info = ref context.GetState(i);
info.InUse = false;
}
Memory<VoiceState>[] voiceStatesArray = ArrayPool<Memory<VoiceState>>.Shared.Rent(Constants.VoiceChannelCountMax);
Span<Memory<VoiceState>> voiceStates = voiceStatesArray.AsSpan(0, Constants.VoiceChannelCountMax);
// Start processing
for (int i = 0; i < context.GetCount(); i++)
{
ref readonly VoiceInParameter1 parameter = ref _inputReader.GetRefOrRefToCopy<VoiceInParameter1>(out _);
voiceStates.Fill(Memory<VoiceState>.Empty);
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
if (parameter.InUse)
{
ref VoiceInfo currentVoiceInfo = ref context.GetState(i);
Span<int> channelResourceIdsSpan = parameter.ChannelResourceIds.AsSpan();
for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++)
{
int channelId = channelResourceIdsSpan[channelResourceIndex];
Debug.Assert(channelId >= 0 && channelId < context.GetCount());
voiceStates[channelResourceIndex] = context.GetUpdateStateForCpu(channelId);
}
if (parameter.IsNew)
{
currentVoiceInfo.Initialize();
}
currentVoiceInfo.UpdateParameters1(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourInfo);
if (updateParameterError.ErrorCode != ResultCode.Success)
{
_behaviourInfo.AppendError(ref updateParameterError);
}
currentVoiceInfo.UpdateWaveBuffers1(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceStates, mapper, ref _behaviourInfo);
foreach (ref ErrorInfo errorInfo in waveBufferUpdateErrorInfos.AsSpan())
{
if (errorInfo.ErrorCode != ResultCode.Success)
{
_behaviourInfo.AppendError(ref errorInfo);
}
}
currentVoiceInfo.WriteOutStatus1(ref outStatus, in parameter, voiceStates);
}
}
ArrayPool<Memory<VoiceState>>.Shared.Return(voiceStatesArray);
int currentOutputSize = _output.Length; int currentOutputSize = _output.Length;
@@ -233,7 +322,12 @@ namespace Ryujinx.Audio.Renderer.Server
public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, PoolMapper mapper) public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, PoolMapper mapper)
{ {
if (_behaviourContext.IsEffectInfoVersion2Supported()) if (_behaviourInfo.IsBiquadFilterParameterFloatSupported())
{
return UpdateEffectsVersion3(context, isAudioRendererActive, mapper);
}
if (_behaviourInfo.IsEffectInfoVersion2Supported())
{ {
return UpdateEffectsVersion2(context, isAudioRendererActive, mapper); return UpdateEffectsVersion2(context, isAudioRendererActive, mapper);
} }
@@ -241,6 +335,60 @@ namespace Ryujinx.Audio.Renderer.Server
return UpdateEffectsVersion1(context, isAudioRendererActive, mapper); return UpdateEffectsVersion1(context, isAudioRendererActive, mapper);
} }
public ResultCode UpdateEffectsVersion3(EffectContext context, bool isAudioRendererActive, PoolMapper mapper)
{
if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion2>() != _inputHeader.EffectsSize)
{
return ResultCode.InvalidUpdateInfo;
}
int initialOutputSize = _output.Length;
long initialInputConsumed = _inputReader.Consumed;
for (int i = 0; i < context.GetCount(); i++)
{
ref readonly EffectInParameterVersion3 parameter = ref _inputReader.GetRefOrRefToCopy<EffectInParameterVersion3>(out _);
ref EffectOutStatusVersion2 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion2>(ref _output)[0];
ref BaseEffect effect = ref context.GetEffect(i);
if (!effect.IsTypeValid(in parameter))
{
ResetEffect(ref effect, in parameter, mapper);
}
effect.Update(out ErrorInfo updateErrorInfo, in parameter, mapper);
if (updateErrorInfo.ErrorCode != ResultCode.Success)
{
_behaviourInfo.AppendError(ref updateErrorInfo);
}
effect.StoreStatus(ref outStatus, isAudioRendererActive);
if (parameter.IsNew)
{
effect.InitializeResultState(ref context.GetDspState(i));
effect.InitializeResultState(ref context.GetState(i));
}
effect.UpdateResultState(ref outStatus.ResultState, ref context.GetState(i));
}
int currentOutputSize = _output.Length;
OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion2>() * context.GetCount());
OutputHeader.TotalSize += OutputHeader.EffectsSize;
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
_inputReader.SetConsumed(initialInputConsumed + _inputHeader.EffectsSize);
return ResultCode.Success;
}
public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, PoolMapper mapper) public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, PoolMapper mapper)
{ {
if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion2>() != _inputHeader.EffectsSize) if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion2>() != _inputHeader.EffectsSize)
@@ -269,7 +417,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (updateErrorInfo.ErrorCode != ResultCode.Success) if (updateErrorInfo.ErrorCode != ResultCode.Success)
{ {
_behaviourContext.AppendError(ref updateErrorInfo); _behaviourInfo.AppendError(ref updateErrorInfo);
} }
effect.StoreStatus(ref outStatus, isAudioRendererActive); effect.StoreStatus(ref outStatus, isAudioRendererActive);
@@ -323,7 +471,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (updateErrorInfo.ErrorCode != ResultCode.Success) if (updateErrorInfo.ErrorCode != ResultCode.Success)
{ {
_behaviourContext.AppendError(ref updateErrorInfo); _behaviourInfo.AppendError(ref updateErrorInfo);
} }
effect.StoreStatus(ref outStatus, isAudioRendererActive); effect.StoreStatus(ref outStatus, isAudioRendererActive);
@@ -382,7 +530,7 @@ namespace Ryujinx.Audio.Renderer.Server
uint inputMixSize; uint inputMixSize;
uint inputSize = 0; uint inputSize = 0;
if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()) if (_behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported())
{ {
ref readonly MixInParameterDirtyOnlyUpdate parameter = ref _inputReader.GetRefOrRefToCopy<MixInParameterDirtyOnlyUpdate>(out _); ref readonly MixInParameterDirtyOnlyUpdate parameter = ref _inputReader.GetRefOrRefToCopy<MixInParameterDirtyOnlyUpdate>(out _);
@@ -421,12 +569,12 @@ namespace Ryujinx.Audio.Renderer.Server
int mixId = i; int mixId = i;
if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()) if (_behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported())
{ {
mixId = parameter.MixId; mixId = parameter.MixId;
} }
ref MixState mix = ref mixContext.GetState(mixId); ref MixInfo mix = ref mixContext.GetState(mixId);
if (parameter.IsUsed != mix.IsUsed) if (parameter.IsUsed != mix.IsUsed)
{ {
@@ -442,13 +590,13 @@ namespace Ryujinx.Audio.Renderer.Server
if (mix.IsUsed) if (mix.IsUsed)
{ {
isMixContextDirty |= mix.Update(mixContext.EdgeMatrix, in parameter, effectContext, splitterContext, _behaviourContext); isMixContextDirty |= mix.Update(mixContext.EdgeMatrix, in parameter, effectContext, splitterContext, _behaviourInfo);
} }
} }
if (isMixContextDirty) if (isMixContextDirty)
{ {
if (_behaviourContext.IsSplitterSupported() && splitterContext.UsingSplitter()) if (_behaviourInfo.IsSplitterSupported() && splitterContext.UsingSplitter())
{ {
if (!mixContext.Sort(splitterContext)) if (!mixContext.Sort(splitterContext))
{ {
@@ -505,7 +653,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (updateErrorInfo.ErrorCode != ResultCode.Success) if (updateErrorInfo.ErrorCode != ResultCode.Success)
{ {
_behaviourContext.AppendError(ref updateErrorInfo); _behaviourInfo.AppendError(ref updateErrorInfo);
} }
} }
@@ -553,7 +701,7 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
ref BehaviourErrorInfoOutStatus outStatus = ref SpanIOHelper.GetWriteRef<BehaviourErrorInfoOutStatus>(ref _output)[0]; ref BehaviourErrorInfoOutStatus outStatus = ref SpanIOHelper.GetWriteRef<BehaviourErrorInfoOutStatus>(ref _output)[0];
_behaviourContext.CopyErrorInfo(outStatus.ErrorInfos.AsSpan(), out outStatus.ErrorInfosCount); _behaviourInfo.CopyErrorInfo(outStatus.ErrorInfos.AsSpan(), out outStatus.ErrorInfosCount);
OutputHeader.BehaviourSize = (uint)Unsafe.SizeOf<BehaviourErrorInfoOutStatus>(); OutputHeader.BehaviourSize = (uint)Unsafe.SizeOf<BehaviourErrorInfoOutStatus>();
OutputHeader.TotalSize += OutputHeader.BehaviourSize; OutputHeader.TotalSize += OutputHeader.BehaviourSize;

View File

@@ -1,7 +1,9 @@
using Ryujinx.Audio.Renderer.Server.Voice;
namespace Ryujinx.Audio.Renderer.Server.Types namespace Ryujinx.Audio.Renderer.Server.Types
{ {
/// <summary> /// <summary>
/// The internal play state of a <see cref="Voice.VoiceState"/> /// The internal play state of a <see cref="VoiceInfo"/>
/// </summary> /// </summary>
public enum PlayState public enum PlayState
{ {
@@ -24,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.Types
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is changed to the <see cref="Stopped"/> state after command generation. /// This is changed to the <see cref="Stopped"/> state after command generation.
/// <seealso cref="Voice.VoiceState.UpdateForCommandGeneration(Voice.VoiceContext)"/> /// <seealso cref="VoiceInfo.UpdateForCommandGeneration(Voice.VoiceContext)"/>
/// </remarks> /// </remarks>
Stopping, Stopping,

View File

@@ -5,7 +5,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
/// <summary> /// <summary>
/// Server state for a upsampling. /// Server state for a upsampling.
/// </summary> /// </summary>
public class UpsamplerState public class UpsamplerInfo
{ {
/// <summary> /// <summary>
/// The output buffer containing the target samples. /// The output buffer containing the target samples.
@@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
public uint SampleCount { get; } public uint SampleCount { get; }
/// <summary> /// <summary>
/// The index of the <see cref="UpsamplerState"/>. (used to free it) /// The index of the <see cref="UpsamplerInfo"/>. (used to free it)
/// </summary> /// </summary>
private readonly int _index; private readonly int _index;
@@ -43,13 +43,13 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
public UpsamplerBufferState[] BufferStates; public UpsamplerBufferState[] BufferStates;
/// <summary> /// <summary>
/// Create a new <see cref="UpsamplerState"/>. /// Create a new <see cref="UpsamplerInfo"/>.
/// </summary> /// </summary>
/// <param name="manager">The upsampler manager.</param> /// <param name="manager">The upsampler manager.</param>
/// <param name="index">The index of the <see cref="UpsamplerState"/>. (used to free it)</param> /// <param name="index">The index of the <see cref="UpsamplerInfo"/>. (used to free it)</param>
/// <param name="outputBuffer">The output buffer used to contain the target samples.</param> /// <param name="outputBuffer">The output buffer used to contain the target samples.</param>
/// <param name="sampleCount">The target sample count.</param> /// <param name="sampleCount">The target sample count.</param>
public UpsamplerState(UpsamplerManager manager, int index, Memory<float> outputBuffer, uint sampleCount) public UpsamplerInfo(UpsamplerManager manager, int index, Memory<float> outputBuffer, uint sampleCount)
{ {
_manager = manager; _manager = manager;
_index = index; _index = index;
@@ -58,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
} }
/// <summary> /// <summary>
/// Release the <see cref="UpsamplerState"/>. /// Release the <see cref="UpsamplerInfo"/>.
/// </summary> /// </summary>
public void Release() public void Release()
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
/// <summary> /// <summary>
/// The upsamplers instances. /// The upsamplers instances.
/// </summary> /// </summary>
private readonly UpsamplerState[] _upsamplers; private readonly UpsamplerInfo[] _upsamplers;
/// <summary> /// <summary>
/// The count of upsamplers. /// The count of upsamplers.
@@ -39,14 +39,14 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
_upSamplerWorkBuffer = upSamplerWorkBuffer; _upSamplerWorkBuffer = upSamplerWorkBuffer;
_count = count; _count = count;
_upsamplers = new UpsamplerState[_count]; _upsamplers = new UpsamplerInfo[_count];
} }
/// <summary> /// <summary>
/// Allocate a new <see cref="UpsamplerState"/>. /// Allocate a new <see cref="UpsamplerInfo"/>.
/// </summary> /// </summary>
/// <returns>A new <see cref="UpsamplerState"/> or null if out of memory.</returns> /// <returns>A new <see cref="UpsamplerInfo"/> or null if out of memory.</returns>
public UpsamplerState Allocate() public UpsamplerInfo Allocate()
{ {
int workBufferOffset = 0; int workBufferOffset = 0;
@@ -56,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
{ {
if (_upsamplers[i] == null) if (_upsamplers[i] == null)
{ {
_upsamplers[i] = new UpsamplerState(this, i, _upSamplerWorkBuffer.Slice(workBufferOffset, Constants.UpSampleEntrySize), Constants.TargetSampleCount); _upsamplers[i] = new UpsamplerInfo(this, i, _upSamplerWorkBuffer.Slice(workBufferOffset, Constants.UpSampleEntrySize), Constants.TargetSampleCount);
return _upsamplers[i]; return _upsamplers[i];
} }
@@ -69,9 +69,9 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
} }
/// <summary> /// <summary>
/// Free a <see cref="UpsamplerState"/> at the given index. /// Free a <see cref="UpsamplerInfo"/> at the given index.
/// </summary> /// </summary>
/// <param name="index">The index of the <see cref="UpsamplerState"/> to free.</param> /// <param name="index">The index of the <see cref="UpsamplerInfo"/> to free.</param>
public void Free(int index) public void Free(int index)
{ {
lock (_lock) lock (_lock)

View File

@@ -11,14 +11,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
public class VoiceContext public class VoiceContext
{ {
/// <summary> /// <summary>
/// Storage of the sorted indices to <see cref="VoiceState"/>. /// Storage of the sorted indices to <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
private Memory<int> _sortedVoices; private Memory<int> _sortedVoices;
/// <summary> /// <summary>
/// Storage for <see cref="VoiceState"/>. /// Storage for <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
private Memory<VoiceState> _voices; private Memory<VoiceInfo> _voices;
/// <summary> /// <summary>
/// Storage for <see cref="VoiceChannelResource"/>. /// Storage for <see cref="VoiceChannelResource"/>.
@@ -26,27 +26,27 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
private Memory<VoiceChannelResource> _voiceChannelResources; private Memory<VoiceChannelResource> _voiceChannelResources;
/// <summary> /// <summary>
/// Storage for <see cref="VoiceUpdateState"/> that are used during audio renderer server updates. /// Storage for <see cref="VoiceState"/> that are used during audio renderer server updates.
/// </summary> /// </summary>
private Memory<VoiceUpdateState> _voiceUpdateStatesCpu; private Memory<VoiceState> _voiceStatesCpu;
/// <summary> /// <summary>
/// Storage for <see cref="VoiceUpdateState"/> for the <see cref="Dsp.AudioProcessor"/>. /// Storage for <see cref="VoiceState"/> for the <see cref="Dsp.AudioProcessor"/>.
/// </summary> /// </summary>
private Memory<VoiceUpdateState> _voiceUpdateStatesDsp; private Memory<VoiceState> _voiceStatesDsp;
/// <summary> /// <summary>
/// The total voice count. /// The total voice count.
/// </summary> /// </summary>
private uint _voiceCount; private uint _voiceCount;
public void Initialize(Memory<int> sortedVoices, Memory<VoiceState> voices, Memory<VoiceChannelResource> voiceChannelResources, Memory<VoiceUpdateState> voiceUpdateStatesCpu, Memory<VoiceUpdateState> voiceUpdateStatesDsp, uint voiceCount) public void Initialize(Memory<int> sortedVoices, Memory<VoiceInfo> voices, Memory<VoiceChannelResource> voiceChannelResources, Memory<VoiceState> voiceStatesCpu, Memory<VoiceState> voiceStatesDsp, uint voiceCount)
{ {
_sortedVoices = sortedVoices; _sortedVoices = sortedVoices;
_voices = voices; _voices = voices;
_voiceChannelResources = voiceChannelResources; _voiceChannelResources = voiceChannelResources;
_voiceUpdateStatesCpu = voiceUpdateStatesCpu; _voiceStatesCpu = voiceStatesCpu;
_voiceUpdateStatesDsp = voiceUpdateStatesDsp; _voiceStatesDsp = voiceStatesDsp;
_voiceCount = voiceCount; _voiceCount = voiceCount;
} }
@@ -70,38 +70,38 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
} }
/// <summary> /// <summary>
/// Get a <see cref="Memory{VoiceUpdateState}"/> at the given <paramref name="id"/>. /// Get a <see cref="Memory{VoiceState}"/> at the given <paramref name="id"/>.
/// </summary> /// </summary>
/// <param name="id">The index to use.</param> /// <param name="id">The index to use.</param>
/// <returns>A <see cref="Memory{VoiceUpdateState}"/> at the given <paramref name="id"/>.</returns> /// <returns>A <see cref="Memory{VoiceState}"/> at the given <paramref name="id"/>.</returns>
/// <remarks>The returned <see cref="Memory{VoiceUpdateState}"/> should only be used when updating the server state.</remarks> /// <remarks>The returned <see cref="Memory{VoiceState}"/> should only be used when updating the server state.</remarks>
public Memory<VoiceUpdateState> GetUpdateStateForCpu(int id) public Memory<VoiceState> GetUpdateStateForCpu(int id)
{ {
return SpanIOHelper.GetMemory(_voiceUpdateStatesCpu, id, _voiceCount); return SpanIOHelper.GetMemory(_voiceStatesCpu, id, _voiceCount);
} }
/// <summary> /// <summary>
/// Get a <see cref="Memory{VoiceUpdateState}"/> at the given <paramref name="id"/>. /// Get a <see cref="Memory{VoiceState}"/> at the given <paramref name="id"/>.
/// </summary> /// </summary>
/// <param name="id">The index to use.</param> /// <param name="id">The index to use.</param>
/// <returns>A <see cref="Memory{VoiceUpdateState}"/> at the given <paramref name="id"/>.</returns> /// <returns>A <see cref="Memory{VoiceState}"/> at the given <paramref name="id"/>.</returns>
/// <remarks>The returned <see cref="Memory{VoiceUpdateState}"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks> /// <remarks>The returned <see cref="Memory{VoiceState}"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks>
public Memory<VoiceUpdateState> GetUpdateStateForDsp(int id) public Memory<VoiceState> GetUpdateStateForDsp(int id)
{ {
return SpanIOHelper.GetMemory(_voiceUpdateStatesDsp, id, _voiceCount); return SpanIOHelper.GetMemory(_voiceStatesDsp, id, _voiceCount);
} }
/// <summary> /// <summary>
/// Get a reference to a <see cref="VoiceState"/> at the given <paramref name="id"/>. /// Get a reference to a <see cref="VoiceInfo"/> at the given <paramref name="id"/>.
/// </summary> /// </summary>
/// <param name="id">The index to use.</param> /// <param name="id">The index to use.</param>
/// <returns>A reference to a <see cref="VoiceState"/> at the given <paramref name="id"/>.</returns> /// <returns>A reference to a <see cref="VoiceInfo"/> at the given <paramref name="id"/>.</returns>
public ref VoiceState GetState(int id) public ref VoiceInfo GetState(int id)
{ {
return ref SpanIOHelper.GetFromMemory(_voices, id, _voiceCount); return ref SpanIOHelper.GetFromMemory(_voices, id, _voiceCount);
} }
public ref VoiceState GetSortedState(int id) public ref VoiceInfo GetSortedState(int id)
{ {
Debug.Assert(id >= 0 && id < _voiceCount); Debug.Assert(id >= 0 && id < _voiceCount);
@@ -113,7 +113,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// </summary> /// </summary>
public void UpdateForCommandGeneration() public void UpdateForCommandGeneration()
{ {
_voiceUpdateStatesDsp.CopyTo(_voiceUpdateStatesCpu); _voiceStatesDsp.CopyTo(_voiceStatesCpu);
} }
/// <summary> /// <summary>
@@ -126,24 +126,24 @@ 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 VoiceInfo aInfo = ref GetState(a);
ref VoiceState bState = ref GetState(b); ref VoiceInfo bInfo = ref GetState(b);
int result = aState.Priority.CompareTo(bState.Priority); int result = aInfo.Priority.CompareTo(bInfo.Priority);
if (result == 0) if (result == 0)
{ {
return aState.SortingOrder.CompareTo(bState.SortingOrder); return aInfo.SortingOrder.CompareTo(bInfo.SortingOrder);
} }
return result; return result;
}); });
sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span); // sortedVoicesTemp.CopyTo(_sortedVoices.Span);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using Ryujinx.Audio.Common; using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.MemoryPool; using Ryujinx.Audio.Renderer.Server.MemoryPool;
@@ -9,13 +10,13 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static Ryujinx.Audio.Renderer.Common.BehaviourParameter; using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter1;
using PlayState = Ryujinx.Audio.Renderer.Server.Types.PlayState; using PlayState = Ryujinx.Audio.Renderer.Server.Types.PlayState;
namespace Ryujinx.Audio.Renderer.Server.Voice namespace Ryujinx.Audio.Renderer.Server.Voice
{ {
[StructLayout(LayoutKind.Sequential, Pack = Alignment)] [StructLayout(LayoutKind.Sequential, Pack = Alignment)]
public struct VoiceState public struct VoiceInfo
{ {
public const int Alignment = 0x10; public const int Alignment = 0x10;
@@ -102,7 +103,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <summary> /// <summary>
/// Biquad filters to apply to the output of the voice. /// Biquad filters to apply to the output of the voice.
/// </summary> /// </summary>
public Array2<BiquadFilterParameter> BiquadFilters; public Array2<BiquadFilterParameter2> BiquadFilters;
/// <summary> /// <summary>
/// Total count of <see cref="WaveBufferInternal"/> of the voice. /// Total count of <see cref="WaveBufferInternal"/> of the voice.
@@ -185,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization); public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
/// <summary> /// <summary>
/// Initialize the <see cref="VoiceState"/>. /// Initialize the <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
public void Initialize() public void Initialize()
{ {
@@ -215,19 +216,21 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
} }
/// <summary> /// <summary>
/// Initialize the <see cref="WaveBuffer"/> in this <see cref="VoiceState"/>. /// Initialize the <see cref="WaveBuffer"/> in this <see cref="VoiceInfo"/>.
/// </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;
} }
} }
@@ -248,13 +251,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
{ {
return MixId != Constants.UnusedMixId || SplitterId != Constants.UnusedSplitterId; return MixId != Constants.UnusedMixId || SplitterId != Constants.UnusedSplitterId;
} }
/// <summary> /// <summary>
/// Indicate if the server voice information needs to be updated. /// Indicate if the server voice information needs to be updated.
/// </summary> /// </summary>
/// <param name="parameter">The user parameter.</param> /// <param name="parameter">The user parameter.</param>
/// <returns>Return true, if the server voice information needs to be updated.</returns> /// <returns>Return true, if the server voice information needs to be updated.</returns>
private readonly bool ShouldUpdateParameters(in VoiceInParameter parameter) private readonly bool ShouldUpdateParameters2(in VoiceInParameter2 parameter)
{ {
if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress) if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress)
{ {
@@ -266,14 +269,31 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
DataSourceStateUnmapped; DataSourceStateUnmapped;
} }
/// <summary>
/// Indicate if the server voice information needs to be updated.
/// </summary>
/// <param name="parameter">The user parameter.</param>
/// <returns>Return true, if the server voice information needs to be updated.</returns>
private readonly bool ShouldUpdateParameters1(in VoiceInParameter1 parameter)
{
if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress)
{
return DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize;
}
return DataSourceStateAddressInfo.CpuAddress != parameter.DataSourceStateAddress ||
DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize ||
DataSourceStateUnmapped;
}
/// <summary> /// <summary>
/// Update the internal state from a user parameter. /// Update the internal state from a user parameter.
/// </summary> /// </summary>
/// <param name="outErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param> /// <param name="outErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
/// <param name="parameter">The user parameter.</param> /// <param name="parameter">The user parameter.</param>
/// <param name="poolMapper">The mapper to use.</param> /// <param name="poolMapper">The mapper to use.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
public void UpdateParameters(out ErrorInfo outErrorInfo, in VoiceInParameter parameter, PoolMapper poolMapper, ref BehaviourContext behaviourContext) public void UpdateParameters2(out ErrorInfo outErrorInfo, in VoiceInParameter2 parameter, PoolMapper poolMapper, ref BehaviourInfo behaviourInfo)
{ {
InUse = parameter.InUse; InUse = parameter.InUse;
Id = parameter.Id; Id = parameter.Id;
@@ -294,14 +314,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
WaveBuffersCount = parameter.WaveBuffersCount; WaveBuffersCount = parameter.WaveBuffersCount;
WaveBuffersIndex = parameter.WaveBuffersIndex; WaveBuffersIndex = parameter.WaveBuffersIndex;
if (behaviourContext.IsFlushVoiceWaveBuffersSupported()) if (behaviourInfo.IsFlushVoiceWaveBuffersSupported())
{ {
FlushWaveBufferCount += parameter.FlushWaveBufferCount; FlushWaveBufferCount += parameter.FlushWaveBufferCount;
} }
MixId = parameter.MixId; MixId = parameter.MixId;
if (behaviourContext.IsSplitterSupported()) if (behaviourInfo.IsSplitterSupported())
{ {
SplitterId = parameter.SplitterId; SplitterId = parameter.SplitterId;
} }
@@ -314,7 +334,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
DecodingBehaviour behaviour = DecodingBehaviour.Default; DecodingBehaviour behaviour = DecodingBehaviour.Default;
if (behaviourContext.IsDecodingBehaviourFlagSupported()) if (behaviourInfo.IsDecodingBehaviourFlagSupported())
{ {
behaviour = parameter.DecodingBehaviourFlags; behaviour = parameter.DecodingBehaviourFlags;
} }
@@ -326,7 +346,78 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
VoiceDropFlag = false; VoiceDropFlag = false;
} }
if (ShouldUpdateParameters(in parameter)) if (ShouldUpdateParameters2(in parameter))
{
DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize);
}
else
{
outErrorInfo = new ErrorInfo();
}
}
/// <summary>
/// Update the internal state from a user parameter.
/// </summary>
/// <param name="outErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
/// <param name="parameter">The user paramter2.</param>
/// <param name="poolMapper">The mapper to use.</param>
/// <param name="behaviourInfo">The behaviour context.</param>
public void UpdateParameters1(out ErrorInfo outErrorInfo, in VoiceInParameter1 parameter, PoolMapper poolMapper, ref BehaviourInfo behaviourInfo)
{
InUse = parameter.InUse;
Id = parameter.Id;
NodeId = parameter.NodeId;
UpdatePlayState(parameter.PlayState);
SrcQuality = parameter.SrcQuality;
Priority = parameter.Priority;
SortingOrder = parameter.SortingOrder;
SampleRate = parameter.SampleRate;
SampleFormat = parameter.SampleFormat;
ChannelsCount = parameter.ChannelCount;
Pitch = parameter.Pitch;
Volume = parameter.Volume;
BiquadFilters[0] = BiquadFilterHelper.ToBiquadFilterParameter2(parameter.BiquadFilters[0]);
BiquadFilters[1] = BiquadFilterHelper.ToBiquadFilterParameter2(parameter.BiquadFilters[1]);
WaveBuffersCount = parameter.WaveBuffersCount;
WaveBuffersIndex = parameter.WaveBuffersIndex;
if (behaviourInfo.IsFlushVoiceWaveBuffersSupported())
{
FlushWaveBufferCount += parameter.FlushWaveBufferCount;
}
MixId = parameter.MixId;
if (behaviourInfo.IsSplitterSupported())
{
SplitterId = parameter.SplitterId;
}
else
{
SplitterId = Constants.UnusedSplitterId;
}
parameter.ChannelResourceIds.AsSpan().CopyTo(ChannelResourceIds.AsSpan());
DecodingBehaviour behaviour = DecodingBehaviour.Default;
if (behaviourInfo.IsDecodingBehaviourFlagSupported())
{
behaviour = parameter.DecodingBehaviourFlags;
}
DecodingBehaviour = behaviour;
if (parameter.ResetVoiceDropFlag)
{
VoiceDropFlag = false;
}
if (ShouldUpdateParameters1(in parameter))
{ {
DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize); DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize);
} }
@@ -373,14 +464,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
PlayState = newServerPlayState; PlayState = newServerPlayState;
} }
/// <summary> /// <summary>
/// Write the status of the voice to the given user output. /// Write the status of the voice to the given user output.
/// </summary> /// </summary>
/// <param name="outStatus">The given user output.</param> /// <param name="outStatus">The given user output.</param>
/// <param name="parameter">The user parameter.</param> /// <param name="parameter">The user parameter.</param>
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="voiceStates">The voice states associated to the <see cref="VoiceInfo"/>.</param>
public void WriteOutStatus(ref VoiceOutStatus outStatus, in VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates) public void WriteOutStatus2(ref VoiceOutStatus outStatus, in VoiceInParameter2 parameter, ReadOnlySpan<Memory<VoiceState>> voiceStates)
{ {
#if DEBUG #if DEBUG
// Sanity check in debug mode of the internal state // Sanity check in debug mode of the internal state
@@ -388,8 +479,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
{ {
for (int i = 1; i < ChannelsCount; i++) for (int i = 1; i < ChannelsCount; i++)
{ {
ref VoiceUpdateState stateA = ref voiceUpdateStates[i - 1].Span[0]; ref VoiceState stateA = ref voiceStates[i - 1].Span[0];
ref VoiceUpdateState stateB = ref voiceUpdateStates[i].Span[0]; ref VoiceState stateB = ref voiceStates[i].Span[0];
Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed); Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed);
Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount); Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount);
@@ -410,7 +501,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
} }
else else
{ {
ref VoiceUpdateState state = ref voiceUpdateStates[0].Span[0]; ref VoiceState state = ref voiceStates[0].Span[0];
outStatus.VoiceDropFlag = VoiceDropFlag; outStatus.VoiceDropFlag = VoiceDropFlag;
outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed; outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed;
@@ -419,19 +510,63 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
} }
/// <summary> /// <summary>
/// Update the internal state of all the <see cref="WaveBuffer"/> of the <see cref="VoiceState"/>. /// Write the status of the voice to the given user output.
/// </summary>
/// <param name="outStatus">The given user output.</param>
/// <param name="parameter">The user parameter.</param>
/// <param name="voiceStates">The voice states associated to the <see cref="VoiceInfo"/>.</param>
public void WriteOutStatus1(ref VoiceOutStatus outStatus, in VoiceInParameter1 parameter, ReadOnlySpan<Memory<VoiceState>> voiceStates)
{
#if DEBUG
// Sanity check in debug mode of the internal state
if (!parameter.IsNew && !IsNew)
{
for (int i = 1; i < ChannelsCount; i++)
{
ref VoiceState stateA = ref voiceStates[i - 1].Span[0];
ref VoiceState stateB = ref voiceStates[i].Span[0];
Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed);
Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount);
Debug.Assert(stateA.Offset == stateB.Offset);
Debug.Assert(stateA.WaveBufferIndex == stateB.WaveBufferIndex);
Debug.Assert(stateA.Fraction == stateB.Fraction);
Debug.Assert(stateA.IsWaveBufferValid.SequenceEqual(stateB.IsWaveBufferValid));
}
}
#endif
if (parameter.IsNew || IsNew)
{
IsNew = true;
outStatus.VoiceDropFlag = false;
outStatus.PlayedWaveBuffersCount = 0;
outStatus.PlayedSampleCount = 0;
}
else
{
ref VoiceState state = ref voiceStates[0].Span[0];
outStatus.VoiceDropFlag = VoiceDropFlag;
outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed;
outStatus.PlayedSampleCount = state.PlayedSampleCount;
}
}
/// <summary>
/// Update the internal state of all the <see cref="WaveBuffer"/> of the <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
/// <param name="errorInfos">An array of <see cref="ErrorInfo"/> used to report errors when mapping any of the <see cref="WaveBuffer"/>.</param> /// <param name="errorInfos">An array of <see cref="ErrorInfo"/> used to report errors when mapping any of the <see cref="WaveBuffer"/>.</param>
/// <param name="parameter">The user parameter.</param> /// <param name="parameter">The user parameter.</param>
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="voiceStates">The voice states associated to the <see cref="VoiceInfo"/>.</param>
/// <param name="mapper">The mapper to use.</param> /// <param name="mapper">The mapper to use.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
public void UpdateWaveBuffers( public void UpdateWaveBuffers2(
out ErrorInfo[] errorInfos, out ErrorInfo[] errorInfos,
in VoiceInParameter parameter, in VoiceInParameter2 parameter,
ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates, ReadOnlySpan<Memory<VoiceState>> voiceStates,
PoolMapper mapper, PoolMapper mapper,
ref BehaviourContext behaviourContext) ref BehaviourInfo behaviourInfo)
{ {
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2]; errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
@@ -441,20 +576,61 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
for (int i = 0; i < parameter.ChannelCount; i++) for (int i = 0; i < parameter.ChannelCount; i++)
{ {
voiceUpdateStates[i].Span[0].IsWaveBufferValid.Clear(); voiceStates[i].Span[0].IsWaveBufferValid.Clear();
} }
} }
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0]; ref VoiceState voiceState = ref voiceStates[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, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
} }
} }
/// <summary> /// <summary>
/// Update the internal state of one of the <see cref="WaveBuffer"/> of the <see cref="VoiceState"/>. /// Update the internal state of all the <see cref="WaveBuffer"/> of the <see cref="VoiceInfo"/>.
/// </summary>
/// <param name="errorInfos">An array of <see cref="ErrorInfo"/> used to report errors when mapping any of the <see cref="WaveBuffer"/>.</param>
/// <param name="parameter">The user parameter.</param>
/// <param name="voiceStates">The voice states associated to the <see cref="VoiceInfo"/>.</param>
/// <param name="mapper">The mapper to use.</param>
/// <param name="behaviourInfo">The behaviour context.</param>
public void UpdateWaveBuffers1(
out ErrorInfo[] errorInfos,
in VoiceInParameter1 parameter,
ReadOnlySpan<Memory<VoiceState>> voiceStates,
PoolMapper mapper,
ref BehaviourInfo behaviourInfo)
{
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
if (parameter.IsNew)
{
InitializeWaveBuffers();
for (int i = 0; i < parameter.ChannelCount; i++)
{
voiceStates[i].Span[0].IsWaveBufferValid.Clear();
}
}
ref VoiceState voiceState = ref voiceStates[0].Span[0];
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
{
UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
}
}
/// <summary>
/// Update the internal state of one of the <see cref="WaveBuffer"/> of the <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
/// <param name="errorInfos">A <see cref="Span{ErrorInfo}"/> used to report errors when mapping the <see cref="WaveBuffer"/>.</param> /// <param name="errorInfos">A <see cref="Span{ErrorInfo}"/> used to report errors when mapping the <see cref="WaveBuffer"/>.</param>
/// <param name="waveBuffer">The <see cref="WaveBuffer"/> to update.</param> /// <param name="waveBuffer">The <see cref="WaveBuffer"/> to update.</param>
@@ -462,7 +638,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <param name="sampleFormat">The <see cref="SampleFormat"/> from the user input.</param> /// <param name="sampleFormat">The <see cref="SampleFormat"/> from the user input.</param>
/// <param name="isValid">If set to true, the server side wavebuffer is considered valid.</param> /// <param name="isValid">If set to true, the server side wavebuffer is considered valid.</param>
/// <param name="mapper">The mapper to use.</param> /// <param name="mapper">The mapper to use.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourInfo">The behaviour context.</param>
private void UpdateWaveBuffer( private void UpdateWaveBuffer(
Span<ErrorInfo> errorInfos, Span<ErrorInfo> errorInfos,
ref WaveBuffer waveBuffer, ref WaveBuffer waveBuffer,
@@ -470,7 +646,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
SampleFormat sampleFormat, SampleFormat sampleFormat,
bool isValid, bool isValid,
PoolMapper mapper, PoolMapper mapper,
ref BehaviourContext behaviourContext) ref BehaviourInfo behaviourInfo)
{ {
if (!isValid && waveBuffer.IsSendToAudioProcessor && waveBuffer.BufferAddressInfo.CpuAddress != 0) if (!isValid && waveBuffer.IsSendToAudioProcessor && waveBuffer.BufferAddressInfo.CpuAddress != 0)
{ {
@@ -497,7 +673,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
errorInfos[0] = bufferInfoError; errorInfos[0] = bufferInfoError;
if (sampleFormat == SampleFormat.Adpcm && behaviourContext.IsAdpcmLoopContextBugFixed() && inputWaveBuffer.ContextAddress != 0) if (sampleFormat == SampleFormat.Adpcm && behaviourInfo.IsAdpcmLoopContextBugFixed() && inputWaveBuffer.ContextAddress != 0)
{ {
bool adpcmLoopContextMapped = mapper.TryAttachBuffer(out ErrorInfo adpcmLoopContextInfoError, bool adpcmLoopContextMapped = mapper.TryAttachBuffer(out ErrorInfo adpcmLoopContextInfoError,
ref waveBuffer.ContextAddressInfo, ref waveBuffer.ContextAddressInfo,
@@ -506,13 +682,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
errorInfos[1] = adpcmLoopContextInfoError; errorInfos[1] = adpcmLoopContextInfoError;
if (adpcmLoopContextMapped) if (!adpcmLoopContextMapped || BufferInfoUnmapped)
{ {
BufferInfoUnmapped = DataSourceStateUnmapped; BufferInfoUnmapped = true;
} }
else else
{ {
BufferInfoUnmapped = true; BufferInfoUnmapped = false;
} }
} }
else else
@@ -529,22 +705,24 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
} }
/// <summary> /// <summary>
/// Reset the resources associated to this <see cref="VoiceState"/>. /// Reset the resources associated to this <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
/// <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);
Debug.Assert(voiceChannelResource.IsUsed); Debug.Assert(voiceChannelResource.IsUsed);
Memory<VoiceUpdateState> dspSharedState = context.GetUpdateStateForDsp(channelResourceId); Memory<VoiceState> dspSharedState = context.GetUpdateStateForDsp(channelResourceId);
MemoryMarshal.Cast<VoiceUpdateState, byte>(dspSharedState.Span).Clear(); MemoryMarshal.Cast<VoiceState, byte>(dspSharedState.Span).Clear();
voiceChannelResource.UpdateState(); voiceChannelResource.UpdateState();
} }
@@ -554,24 +732,31 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// Flush a certain amount of <see cref="WaveBuffer"/>. /// Flush a certain amount of <see cref="WaveBuffer"/>.
/// </summary> /// </summary>
/// <param name="waveBufferCount">The amount of wavebuffer to flush.</param> /// <param name="waveBufferCount">The amount of wavebuffer to flush.</param>
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="voiceStates">The voice states associated to the <see cref="VoiceInfo"/>.</param>
/// <param name="channelCount">The channel count from user input.</param> /// <param name="channelCount">The channel count from user input.</param>
private void FlushWaveBuffers(uint waveBufferCount, Memory<VoiceUpdateState>[] voiceUpdateStates, uint channelCount) private void FlushWaveBuffers(uint waveBufferCount, Memory<VoiceState>[] voiceStates, 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++)
{ {
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0]; ref VoiceState voiceState = ref voiceStates[j].Span[0];
voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount; if (!waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor || voiceState.IsWaveBufferValid[(int)waveBufferIndex])
voiceUpdateState.WaveBufferConsumed++; {
voiceUpdateState.IsWaveBufferValid[(int)waveBufferIndex] = false; voiceState.WaveBufferIndex = (voiceState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
voiceState.WaveBufferConsumed++;
voiceState.IsWaveBufferValid[(int)waveBufferIndex] = false;
}
} }
waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor = true;
waveBufferIndex = (waveBufferIndex + 1) % Constants.VoiceWaveBufferCount; waveBufferIndex = (waveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
} }
@@ -580,44 +765,48 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <summary> /// <summary>
/// Update the internal parameters for command generation. /// Update the internal parameters for command generation.
/// </summary> /// </summary>
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="voiceStates">The voice states associated to the <see cref="VoiceInfo"/>.</param>
/// <returns>Return true if this voice should be played.</returns> /// <returns>Return true if this voice should be played.</returns>
public bool UpdateParametersForCommandGeneration(Memory<VoiceUpdateState>[] voiceUpdateStates) public bool UpdateParametersForCommandGeneration(Memory<VoiceState>[] voiceStates)
{ {
if (FlushWaveBufferCount != 0) if (FlushWaveBufferCount != 0)
{ {
FlushWaveBuffers(FlushWaveBufferCount, voiceUpdateStates, ChannelsCount); FlushWaveBuffers(FlushWaveBufferCount, voiceStates, ChannelsCount);
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++)
{ {
Debug.Assert(!voiceUpdateStates[y].Span[0].IsWaveBufferValid[i]); Debug.Assert(!voiceStates[y].Span[0].IsWaveBufferValid[i]);
voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true; voiceStates[y].Span[0].IsWaveBufferValid[i] = true;
} }
wavebuffer.IsSendToAudioProcessor = true; waveBuffer.IsSendToAudioProcessor = true;
} }
} }
WasPlaying = false; WasPlaying = false;
ref VoiceUpdateState primaryVoiceUpdateState = ref voiceUpdateStates[0].Span[0]; ref VoiceState primaryVoiceState = ref voiceStates[0].Span[0];
for (int i = 0; i < primaryVoiceUpdateState.IsWaveBufferValid.Length; i++) for (int i = 0; i < primaryVoiceState.IsWaveBufferValid.Length; i++)
{ {
if (primaryVoiceUpdateState.IsWaveBufferValid[i]) if (primaryVoiceState.IsWaveBufferValid[i])
{ {
return true; return true;
} }
@@ -626,35 +815,37 @@ 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++)
{ {
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0]; ref VoiceState voiceState = ref voiceStates[j].Span[0];
if (voiceUpdateState.IsWaveBufferValid[i]) if (voiceState.IsWaveBufferValid[i])
{ {
voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount; voiceState.WaveBufferIndex = (voiceState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
voiceUpdateState.WaveBufferConsumed++; voiceState.WaveBufferConsumed++;
} }
voiceUpdateState.IsWaveBufferValid[i] = false; voiceState.IsWaveBufferValid[i] = false;
} }
} }
for (int i = 0; i < ChannelsCount; i++) for (int i = 0; i < ChannelsCount; i++)
{ {
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[i].Span[0]; ref VoiceState voiceState = ref voiceStates[i].Span[0];
voiceUpdateState.Offset = 0; voiceState.Offset = 0;
voiceUpdateState.PlayedSampleCount = 0; voiceState.PlayedSampleCount = 0;
voiceUpdateState.Pitch.AsSpan().Clear(); voiceState.Pitch.AsSpan().Clear();
voiceUpdateState.Fraction = 0; voiceState.Fraction = 0;
voiceUpdateState.LoopContext = new AdpcmLoopContext(); voiceState.LoopContext = new AdpcmLoopContext();
} }
PlayState = PlayState.Stopped; PlayState = PlayState.Stopped;
@@ -700,14 +891,16 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
IsNew = false; IsNew = false;
} }
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax]; Memory<VoiceState>[] voiceStates = new Memory<VoiceState>[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]); voiceStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]);
} }
return UpdateParametersForCommandGeneration(voiceUpdateStates); return UpdateParametersForCommandGeneration(voiceStates);
} }
} }
} }

View File

@@ -0,0 +1,226 @@
namespace Ryujinx.Common.Collections
{
/// <summary>
/// Represents a collection that can store 1 bit values.
/// </summary>
public struct BitMap
{
/// <summary>
/// Size in bits of the integer used internally for the groups of bits.
/// </summary>
public const int IntSize = 64;
private const int IntShift = 6;
private const int IntMask = IntSize - 1;
private readonly long[] _masks;
/// <summary>
/// Gets or sets the value of a bit.
/// </summary>
/// <param name="bit">Bit to access</param>
/// <returns>Bit value</returns>
public bool this[int bit]
{
get => IsSet(bit);
set
{
if (value)
{
Set(bit);
}
else
{
Clear(bit);
}
}
}
/// <summary>
/// Creates a new bitmap.
/// </summary>
/// <param name="count">Total number of bits</param>
public BitMap(int count)
{
_masks = new long[(count + IntMask) / IntSize];
}
/// <summary>
/// Checks if any bit is set.
/// </summary>
/// <returns>True if any bit is set, false otherwise</returns>
public bool AnySet()
{
for (int i = 0; i < _masks.Length; i++)
{
if (_masks[i] != 0)
{
return true;
}
}
return false;
}
/// <summary>
/// Checks if a specific bit is set.
/// </summary>
/// <param name="bit">Bit to be checked</param>
/// <returns>True if set, false otherwise</returns>
public bool IsSet(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
return (_masks[wordIndex] & wordMask) != 0;
}
/// <summary>
/// Checks if any bit inside a given range of bits is set.
/// </summary>
/// <param name="start">Start bit of the range</param>
/// <param name="end">End bit of the range (inclusive)</param>
/// <returns>True if any bit is set, false otherwise</returns>
public bool IsSet(int start, int end)
{
if (start == end)
{
return IsSet(start);
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
return (_masks[startIndex] & startMask & endMask) != 0;
}
if ((_masks[startIndex] & startMask) != 0)
{
return true;
}
for (int i = startIndex + 1; i < endIndex; i++)
{
if (_masks[i] != 0)
{
return true;
}
}
if ((_masks[endIndex] & endMask) != 0)
{
return true;
}
return false;
}
/// <summary>
/// Sets the value of a bit to 1.
/// </summary>
/// <param name="bit">Bit to be set</param>
/// <returns>True if the bit was 0 and then changed to 1, false if it was already 1</returns>
public bool Set(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
if ((_masks[wordIndex] & wordMask) != 0)
{
return false;
}
_masks[wordIndex] |= wordMask;
return true;
}
/// <summary>
/// Sets a given range of bits to 1.
/// </summary>
/// <param name="start">Start bit of the range</param>
/// <param name="end">End bit of the range (inclusive)</param>
public void SetRange(int start, int end)
{
if (start == end)
{
Set(start);
return;
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
_masks[startIndex] |= startMask & endMask;
}
else
{
_masks[startIndex] |= startMask;
for (int i = startIndex + 1; i < endIndex; i++)
{
_masks[i] |= -1;
}
_masks[endIndex] |= endMask;
}
}
/// <summary>
/// Sets a given bit to 0.
/// </summary>
/// <param name="bit">Bit to be cleared</param>
public void Clear(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
_masks[wordIndex] &= ~wordMask;
}
/// <summary>
/// Sets all bits to 0.
/// </summary>
public void Clear()
{
for (int i = 0; i < _masks.Length; i++)
{
_masks[i] = 0;
}
}
/// <summary>
/// Sets one or more groups of bits to 0.
/// See <see cref="IntSize"/> for how many bits are inside each group.
/// </summary>
/// <param name="start">Start index of the group</param>
/// <param name="end">End index of the group (inclusive)</param>
public void ClearInt(int start, int end)
{
for (int i = start; i <= end; i++)
{
_masks[i] = 0;
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -46,10 +46,11 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
// PS5 touchpad button // PS5 touchpad button
Touchpad, Touchpad,
// Virtual buttons for single joycon // Virtual buttons for single joycon (left)
SingleLeftTrigger0, SingleLeftTrigger0,
SingleRightTrigger0, SingleRightTrigger0,
// Virtual buttons for single joycon (right)
SingleLeftTrigger1, SingleLeftTrigger1,
SingleRightTrigger1, SingleRightTrigger1,

View File

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

View File

@@ -187,6 +187,17 @@ namespace Ryujinx.Common.Logging
} }
} }
public static void Flush()
{
foreach (ILogTarget target in _logTargets)
{
if (target is AsyncLogTargetWrapper asyncTarget)
{
asyncTarget.Flush();
}
}
}
public static void Shutdown() public static void Shutdown()
{ {
Updated = null; Updated = null;

View File

@@ -27,6 +27,17 @@ namespace Ryujinx.Common.Logging.Targets
private readonly int _overflowTimeout; private readonly int _overflowTimeout;
private sealed class FlushEventArgs : LogEventArgs
{
public readonly ManualResetEventSlim SignalEvent;
public FlushEventArgs(ManualResetEventSlim signalEvent)
: base(LogLevel.Notice, TimeSpan.Zero, string.Empty, string.Empty)
{
SignalEvent = signalEvent;
}
}
string ILogTarget.Name => _target.Name; string ILogTarget.Name => _target.Name;
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block) public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
@@ -41,7 +52,15 @@ namespace Ryujinx.Common.Logging.Targets
{ {
try try
{ {
_target.Log(this, _messageQueue.Take()); LogEventArgs item = _messageQueue.Take();
if (item is FlushEventArgs flush)
{
flush.SignalEvent.Set();
continue;
}
_target.Log(this, item);
} }
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
@@ -68,6 +87,26 @@ namespace Ryujinx.Common.Logging.Targets
} }
} }
public void Flush()
{
if (_messageQueue.Count == 0 || _messageQueue.IsAddingCompleted)
{
return;
}
using var signal = new ManualResetEventSlim(false);
try
{
_messageQueue.Add(new FlushEventArgs(signal));
}
catch (InvalidOperationException)
{
return;
}
signal.Wait();
}
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);

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";
} }
} }

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