Compare commits
204 Commits
Author | SHA1 | Date | |
---|---|---|---|
7d158acc3b | |||
5dbba07e33 | |||
d86249cb0a | |||
04d68ca616 | |||
050f22977f | |||
319507f2a1 | |||
d717aef2be | |||
24ee8c39f1 | |||
73f985d27c | |||
ef81658fbd | |||
062ef43eb4 | |||
eb8132b627 | |||
ccf96bf5e6 | |||
f39e89ece7 | |||
cf77c011e4 | |||
cd74ae1bbd | |||
62216782ca | |||
2f36a6665c | |||
ca59c3f499 | |||
fdd7ee791c | |||
398fa1c238 | |||
2c5c0392f9 | |||
e0acde04bb | |||
3c61d560c3 | |||
b45a81458a | |||
460f9faf4e | |||
552c15739c | |||
0137c9e635 | |||
23fa5f4c9c | |||
4f75e26ec7 | |||
8d8983049e | |||
7969fb6bba | |||
4a4b11871e | |||
e85ee673b1 | |||
42f22fe5d7 | |||
263eb97f79 | |||
3004902257 | |||
59ddb26628 | |||
83fda10f6e | |||
d97e995e59 | |||
56b2f84702 | |||
698e36bbd2 | |||
6ce49a2dc7 | |||
ccd330ba0f | |||
95d252b7b8 | |||
add681144b | |||
c6dc00815a | |||
99f04ac1a6 | |||
ce09450743 | |||
2cb80f37d4 | |||
827069e784 | |||
1a919e99b2 | |||
f77bebac80 | |||
6fbf279fac | |||
344f4f52c1 | |||
eb212aa91b | |||
a6dbb2ad2b | |||
595e514f18 | |||
07435ad844 | |||
1668ba913f | |||
a830eb666b | |||
cfc75d7e78 | |||
c525d7d9a9 | |||
1a0a351a15 | |||
bd3335c143 | |||
a94445b23e | |||
0c3421973c | |||
0afa8f2c14 | |||
d25a084858 | |||
311ca3c3f1 | |||
3193ef1083 | |||
5a878ae9af | |||
1828bc949e | |||
c0f2491eae | |||
d7c6474729 | |||
1ecc8fbc3b | |||
888402ecaf | |||
971d24aef0 | |||
c41fddd25e | |||
2ebe929fa5 | |||
53d096e392 | |||
4cc00bb4b1 | |||
c98b7fc702 | |||
e65effcb05 | |||
c1ed150949 | |||
c634eb4054 | |||
eb1ce41b00 | |||
2f427deb67 | |||
8f51938e2b | |||
4d84df9487 | |||
9ec8b2c01a | |||
091230af22 | |||
3aea194606 | |||
cdccf89e10 | |||
2ca0b17339 | |||
47639e6eeb | |||
cd78adf07f | |||
a3dc295c5f | |||
2ef4f92b07 | |||
2b6cc4b353 | |||
075575200d | |||
3a3b51893e | |||
44dbab3848 | |||
cada4d04ef | |||
e9edf0ab7f | |||
6e40b64554 | |||
1a676ee913 | |||
a23d8cb92f | |||
ab12fbe963 | |||
d0cc13ce0b | |||
65c035cdf8 | |||
56c5dbe557 | |||
5976a5161b | |||
3d4dea624d | |||
89a274c6a6 | |||
c6f8bfed90 | |||
9b94662b4b | |||
216026c096 | |||
7070cf6ae5 | |||
9839cd56fb | |||
99f46e22e2 | |||
22fb8c9d4f | |||
2f93ae9a19 | |||
3224ddeeb4 | |||
446f2854a5 | |||
8884d1fd73 | |||
268c9aecf8 | |||
e916662b0f | |||
2ddd3dd4a7 | |||
a8f7ababb5 | |||
22e3ff06b5 | |||
9480e5c5ce | |||
0652813b0f | |||
e7f2342eba | |||
543d75a587 | |||
338ff79e1e | |||
80201466ae | |||
7a971edb57 | |||
c1b0ab805a | |||
3e6e0e4afa | |||
808803d97a | |||
ead9a25141 | |||
3e0d67533f | |||
0b55914864 | |||
451a28afb8 | |||
12b235700c | |||
3be616207d | |||
791bf22109 | |||
66b1d59c66 | |||
c8bb05633e | |||
fb1171a21e | |||
22c0aa9c90 | |||
6d28b64312 | |||
05c041feeb | |||
8c2da1aa04 | |||
5def0429f8 | |||
8e74fa3456 | |||
6208c3e6f0 | |||
7124d679fd | |||
b323a01738 | |||
f6d24449b6 | |||
72bdc24db8 | |||
43514771bf | |||
dbfe859ed7 | |||
c94a73ec60 | |||
20a280525f | |||
75a4ea3370 | |||
d26ef2eec3 | |||
a0552fd78b | |||
bb8c5ebae1 | |||
50bdda5baa | |||
18df25f66f | |||
e19e7622a3 | |||
26026d1357 | |||
24068b023c | |||
1217a8e69b | |||
732db7581f | |||
fdd3263e31 | |||
ce607db944 | |||
6b4ee82e5d | |||
e2a655f1a4 | |||
d9a18919b0 | |||
8354434a37 | |||
5a900f38c5 | |||
a3a63d4394 | |||
3924bd1a43 | |||
50458b2472 | |||
dda0f26067 | |||
4e1a60328e | |||
2505a1abcd | |||
bc4d99a078 | |||
ec6cb0abb4 | |||
53b5985da6 | |||
87f238be60 | |||
4d0dbbfae2 | |||
08384ee5a8 | |||
d4d0a48bfe | |||
57d8afd0c9 | |||
c43fb92bbf | |||
167f50bbcd | |||
ba91f5d401 | |||
79f6c18a9b | |||
4f63782bac | |||
6f5fcb7970 |
@ -259,12 +259,12 @@ dotnet_diagnostic.CA1861.severity = none
|
||||
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
|
||||
dotnet_diagnostic.CA1862.severity = none
|
||||
|
||||
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
||||
# Disable "mark members as static" rule for services
|
||||
[src/Ryujinx/UI/ViewModels/**.cs]
|
||||
# Disable "mark members as static" rule for ViewModels
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
|
||||
[src/Ryujinx.Ava/UI/ViewModels/**.cs]
|
||||
# Disable "mark members as static" rule for ViewModels
|
||||
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
||||
# Disable "mark members as static" rule for services
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
|
||||
[src/Ryujinx.Tests/Cpu/*.cs]
|
||||
|
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -23,7 +23,7 @@ body:
|
||||
attributes:
|
||||
label: Log file
|
||||
description: A log file will help our developers to better diagnose and fix the issue.
|
||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@ -83,4 +83,4 @@ body:
|
||||
- Additional info about your environment:
|
||||
- Any other information relevant to your issue.
|
||||
validations:
|
||||
required: false
|
||||
required: false
|
||||
|
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,6 +1,7 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature for Ryujinx.
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
body:
|
||||
- type: textarea
|
||||
id: overview
|
||||
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -7,7 +7,7 @@ updates:
|
||||
labels:
|
||||
- "infra"
|
||||
reviewers:
|
||||
- marysaka
|
||||
- TSRBerry
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
|
||||
@ -19,7 +19,7 @@ updates:
|
||||
labels:
|
||||
- "infra"
|
||||
reviewers:
|
||||
- marysaka
|
||||
- TSRBerry
|
||||
commit-message:
|
||||
prefix: nuget
|
||||
groups:
|
||||
|
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
@ -20,7 +20,7 @@ gpu:
|
||||
|
||||
gui:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
|
||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**', 'src/Ryujinx.Gtk3/**']
|
||||
|
||||
horizon:
|
||||
- changed-files:
|
||||
|
7
.github/reviewers.yml
vendored
7
.github/reviewers.yml
vendored
@ -1,31 +1,24 @@
|
||||
audio:
|
||||
- marysaka
|
||||
|
||||
cpu:
|
||||
- gdkchan
|
||||
- riperiperi
|
||||
- marysaka
|
||||
- LDj3SNuD
|
||||
|
||||
gpu:
|
||||
- gdkchan
|
||||
- riperiperi
|
||||
- marysaka
|
||||
|
||||
gui:
|
||||
- Ack77
|
||||
- emmauss
|
||||
- TSRBerry
|
||||
- marysaka
|
||||
|
||||
horizon:
|
||||
- gdkchan
|
||||
- Ack77
|
||||
- marysaka
|
||||
- TSRBerry
|
||||
|
||||
infra:
|
||||
- marysaka
|
||||
- TSRBerry
|
||||
|
||||
default:
|
||||
|
40
.github/workflows/build.yml
vendored
40
.github/workflows/build.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||
- { name: osx-x64, os: macOS-latest, zip_os_name: osx_x64 }
|
||||
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
|
||||
|
||||
fail-fast: false
|
||||
steps:
|
||||
@ -41,12 +41,12 @@ jobs:
|
||||
- name: Change config filename
|
||||
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
- name: Change config filename for macOS
|
||||
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'macOS-latest'
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
|
||||
|
||||
- name: Build
|
||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||
@ -61,21 +61,21 @@ jobs:
|
||||
|
||||
- name: Publish Ryujinx
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
- name: Publish Ryujinx.Headless.SDL2
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
- name: Publish Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
- name: Publish Ryujinx.Gtk3
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
- name: Set executable bit
|
||||
run: |
|
||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||
chmod +x ./publish_gtk/Ryujinx.Gtk3 ./publish_gtk/Ryujinx.sh
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
|
||||
|
||||
- name: Upload Ryujinx artifact
|
||||
@ -83,21 +83,21 @@ jobs:
|
||||
with:
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
||||
path: publish
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
||||
path: publish_sdl2_headless
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
- name: Upload Ryujinx.Gtk3 artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
||||
path: publish_ava
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
||||
name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
||||
path: publish_gtk
|
||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||
|
||||
build_macos:
|
||||
name: macOS Universal (${{ matrix.configuration }})
|
||||
@ -140,19 +140,19 @@ jobs:
|
||||
shell: bash
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Publish macOS Ryujinx.Ava
|
||||
- name: Publish macOS Ryujinx
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
|
||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish_ava/*.tar.gz"
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish/*.tar.gz"
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
|
10
.github/workflows/nightly_pr_comment.yml
vendored
10
.github/workflows/nightly_pr_comment.yml
vendored
@ -39,24 +39,24 @@ jobs:
|
||||
return core.error(`No artifacts found`);
|
||||
}
|
||||
let body = `Download the artifacts for this pull request:\n`;
|
||||
let hidden_avalonia_artifacts = `\n\n <details><summary>Experimental GUI (Avalonia)</summary>\n`;
|
||||
let hidden_gtk_artifacts = `\n\n <details><summary>Old GUI (GTK3)</summary>\n`;
|
||||
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||
for (const art of artifacts) {
|
||||
if(art.name.includes('Debug')) {
|
||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
} else if(art.name.includes('ava-ryujinx')) {
|
||||
hidden_avalonia_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
} else if(art.name.includes('gtk-ryujinx')) {
|
||||
hidden_gtk_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
||||
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
} else {
|
||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
}
|
||||
}
|
||||
hidden_avalonia_artifacts += `\n</details>`;
|
||||
hidden_gtk_artifacts += `\n</details>`;
|
||||
hidden_headless_artifacts += `\n</details>`;
|
||||
hidden_debug_artifacts += `\n</details>`;
|
||||
body += hidden_avalonia_artifacts;
|
||||
body += hidden_gtk_artifacts;
|
||||
body += hidden_headless_artifacts;
|
||||
body += hidden_debug_artifacts;
|
||||
|
||||
|
39
.github/workflows/release.yml
vendored
39
.github/workflows/release.yml
vendored
@ -44,6 +44,17 @@ jobs:
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
omitBodyDuringUpdate: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
release:
|
||||
name: Release for ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
@ -60,7 +71,7 @@ jobs:
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
|
||||
- name: Overwrite csc problem matcher
|
||||
run: echo "::add-matcher::.github/csc.json"
|
||||
|
||||
@ -86,43 +97,37 @@ jobs:
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_gtk/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||
|
||||
- name: Packing Windows builds
|
||||
if: matrix.platform.os == 'windows-latest'
|
||||
run: |
|
||||
pushd publish_gtk
|
||||
pushd publish_ava
|
||||
cp publish/Ryujinx.exe publish/Ryujinx.Ava.exe
|
||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||
popd
|
||||
|
||||
pushd publish_sdl2_headless
|
||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||
popd
|
||||
|
||||
pushd publish_ava
|
||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Packing Linux builds
|
||||
if: matrix.platform.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd publish_gtk
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||
pushd publish_ava
|
||||
cp publish/Ryujinx publish/Ryujinx.Ava
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava publish/Ryujinx
|
||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_sdl2_headless
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_ava
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Pushing new release
|
||||
@ -183,10 +188,10 @@ jobs:
|
||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
|
||||
- name: Publish macOS Ryujinx.Ava
|
||||
- name: Publish macOS Ryujinx
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||
|
||||
|
||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||
|
@ -3,26 +3,26 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.13" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.13" />
|
||||
<PackageVersion Include="Avalonia" Version="11.0.10" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="DynamicData" Version="8.3.27" />
|
||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.3.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
@ -39,15 +39,14 @@
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.1" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -36,8 +36,8 @@
|
||||
|
||||
## Compatibility
|
||||
|
||||
As of October 2023, Ryujinx has been tested on approximately 4,200 titles;
|
||||
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable.
|
||||
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
|
||||
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
|
||||
|
||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||
|
||||
|
10
Ryujinx.sln
10
Ryujinx.sln
@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32228.430
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Gtk3", "src\Ryujinx.Gtk3\Ryujinx.Gtk3.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||
EndProject
|
||||
@ -69,7 +69,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "sr
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||
EndProject
|
||||
@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -249,6 +251,10 @@ Global
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -4,6 +4,8 @@
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></Policy></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>
|
||||
|
@ -6,10 +6,6 @@ if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||
fi
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Ava"
|
||||
fi
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
||||
RYUJINX_BIN="Ryujinx"
|
||||
fi
|
||||
|
@ -14,8 +14,8 @@ mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
|
||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||
|
||||
# Copy executables first
|
||||
cp "$PUBLISH_DIRECTORY/Ryujinx.Ava" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||
# Copy executable and nsure executable can be executed
|
||||
cp "$PUBLISH_DIRECTORY/Ryujinx" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||
|
||||
# Then all libraries
|
||||
|
@ -22,9 +22,9 @@ EXTRA_ARGS=$8
|
||||
|
||||
if [ "$VERSION" == "1.1.0" ];
|
||||
then
|
||||
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||
else
|
||||
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$VERSION-macos_universal.app.tar
|
||||
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar
|
||||
fi
|
||||
|
||||
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
|
||||
@ -38,9 +38,9 @@ mkdir -p "$TEMP_DIRECTORY"
|
||||
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
||||
|
||||
dotnet restore
|
||||
dotnet build -c "$CONFIGURATION" src/Ryujinx.Ava
|
||||
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava
|
||||
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava
|
||||
dotnet build -c "$CONFIGURATION" src/Ryujinx
|
||||
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
|
||||
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
|
||||
|
||||
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||
@ -108,6 +108,13 @@ tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf "$RELEASE_TAR_FILE_NAME"
|
||||
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
||||
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||
rm "$RELEASE_TAR_FILE_NAME"
|
||||
|
||||
# Create legacy update package for Avalonia to not left behind old testers.
|
||||
if [ "$VERSION" != "1.1.0" ];
|
||||
then
|
||||
cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
echo "Done"
|
@ -33,8 +33,3 @@ Project Docs
|
||||
=================
|
||||
|
||||
To be added. Many project files will contain basic XML docs for key functions and classes in the meantime.
|
||||
|
||||
Other Information
|
||||
=================
|
||||
|
||||
- N/A
|
||||
|
@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
long originalPosition = _stream.Position;
|
||||
|
||||
_stream.Seek(0, SeekOrigin.Begin);
|
||||
_stream.Read(code, 0, code.Length);
|
||||
_stream.ReadExactly(code, 0, code.Length);
|
||||
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||
|
||||
RelocInfo relocInfo;
|
||||
|
@ -251,7 +251,20 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
}
|
||||
}
|
||||
|
||||
int selectedReg = GetHighestValueIndex(freePositions);
|
||||
// If this is a copy destination variable, we prefer the register used for the copy source.
|
||||
// If the register is available, then the copy can be eliminated later as both source
|
||||
// and destination will use the same register.
|
||||
int selectedReg;
|
||||
|
||||
if (current.TryGetCopySourceRegister(out int preferredReg) && freePositions[preferredReg] >= current.GetEnd())
|
||||
{
|
||||
selectedReg = preferredReg;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedReg = GetHighestValueIndex(freePositions);
|
||||
}
|
||||
|
||||
int selectedNextUse = freePositions[selectedReg];
|
||||
|
||||
// Intervals starts and ends at odd positions, unless they span an entire
|
||||
@ -431,7 +444,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetHighestValueIndex(Span<int> span)
|
||||
private static int GetHighestValueIndex(ReadOnlySpan<int> span)
|
||||
{
|
||||
int highest = int.MinValue;
|
||||
|
||||
@ -798,12 +811,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
// The "visited" state is stored in the MSB of the local's value.
|
||||
const ulong VisitedMask = 1ul << 63;
|
||||
|
||||
bool IsVisited(Operand local)
|
||||
static bool IsVisited(Operand local)
|
||||
{
|
||||
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
||||
}
|
||||
|
||||
void SetVisited(Operand local)
|
||||
static void SetVisited(Operand local)
|
||||
{
|
||||
local.GetValueUnsafe() |= VisitedMask;
|
||||
}
|
||||
@ -826,9 +839,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
dest.NumberLocal(_intervals.Count);
|
||||
|
||||
_intervals.Add(new LiveInterval(dest));
|
||||
LiveInterval interval = new LiveInterval(dest);
|
||||
_intervals.Add(interval);
|
||||
|
||||
SetVisited(dest);
|
||||
|
||||
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
||||
// This is used for register preferencing later on, which allows the copy to be eliminated
|
||||
// in some cases.
|
||||
if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
||||
{
|
||||
Operand source = node.GetSource(0);
|
||||
|
||||
if (source.Kind == OperandKind.LocalVariable &&
|
||||
source.GetLocalNumber() > 0 &&
|
||||
(node.Instruction == Instruction.Copy || source.Type == OperandType.I32))
|
||||
{
|
||||
interval.SetCopySource(_intervals[source.GetLocalNumber()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
public LiveRange CurrRange;
|
||||
|
||||
public LiveInterval Parent;
|
||||
public LiveInterval CopySource;
|
||||
|
||||
public UseList Uses;
|
||||
public LiveIntervalList Children;
|
||||
@ -37,6 +38,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
private ref LiveRange CurrRange => ref _data->CurrRange;
|
||||
private ref LiveRange PrevRange => ref _data->PrevRange;
|
||||
private ref LiveInterval Parent => ref _data->Parent;
|
||||
private ref LiveInterval CopySource => ref _data->CopySource;
|
||||
private ref UseList Uses => ref _data->Uses;
|
||||
private ref LiveIntervalList Children => ref _data->Children;
|
||||
|
||||
@ -78,6 +80,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
Register = register;
|
||||
}
|
||||
|
||||
public void SetCopySource(LiveInterval copySource)
|
||||
{
|
||||
CopySource = copySource;
|
||||
}
|
||||
|
||||
public bool TryGetCopySourceRegister(out int copySourceRegIndex)
|
||||
{
|
||||
if (CopySource._data != null)
|
||||
{
|
||||
copySourceRegIndex = CopySource.Register.Index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
copySourceRegIndex = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
PrevRange = default;
|
||||
|
@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||
|
||||
_stream.Read(buffer);
|
||||
_stream.ReadExactly(buffer);
|
||||
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
||||
|
||||
codeStream.Write(buffer);
|
||||
|
@ -746,6 +746,7 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
||||
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||
SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create);
|
||||
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
|
||||
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
||||
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
||||
@ -822,6 +823,10 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
|
||||
SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
|
||||
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
||||
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
||||
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
||||
@ -1007,6 +1012,8 @@ namespace ARMeilleure.Decoders
|
||||
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||
SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
@ -1028,8 +1035,10 @@ namespace ARMeilleure.Decoders
|
||||
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
||||
SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 encoding.
|
||||
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||
@ -1054,6 +1063,7 @@ namespace ARMeilleure.Decoders
|
||||
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
||||
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
|
||||
SetAsimd("111100111x110010xxxx00000xx0xxxx", InstName.Vswp, InstEmit32.Vswp, OpCode32Simd.Create, OpCode32Simd.CreateT32);
|
||||
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
||||
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||
|
@ -2,6 +2,8 @@ using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
@ -19,6 +21,12 @@ namespace ARMeilleure.Instructions
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
if (op.Rn == RegisterAlias.Aarch32Pc && op is OpCodeT32AluImm12)
|
||||
{
|
||||
// For ADR, PC is always 4 bytes aligned, even in Thumb mode.
|
||||
n = context.BitwiseAnd(n, Const(~3u));
|
||||
}
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
@ -284,6 +292,16 @@ namespace ARMeilleure.Instructions
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Qadd16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitSigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Rbit(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
@ -467,6 +485,12 @@ namespace ARMeilleure.Instructions
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
if (op.Rn == RegisterAlias.Aarch32Pc && op is OpCodeT32AluImm12)
|
||||
{
|
||||
// For ADR, PC is always 4 bytes aligned, even in Thumb mode.
|
||||
n = context.BitwiseAnd(n, Const(~3u));
|
||||
}
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
@ -546,6 +570,46 @@ namespace ARMeilleure.Instructions
|
||||
EmitHsub8(context, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Uqadd16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqadd(context, d, context.Add(n, m), 16);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Uqadd8(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqadd(context, d, context.Add(n, m), 8);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Uqsub16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Uqsub8(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||
{
|
||||
EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Usat(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
@ -922,6 +986,251 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSaturateRange(ArmEmitterContext context, Operand result, Operand value, uint saturateTo, bool unsigned, bool setQ = true)
|
||||
{
|
||||
Debug.Assert(saturateTo <= 32);
|
||||
Debug.Assert(!unsigned || saturateTo < 32);
|
||||
|
||||
if (!unsigned && saturateTo == 32)
|
||||
{
|
||||
// No saturation possible for this case.
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (saturateTo == 0)
|
||||
{
|
||||
// Result is always zero if we saturate 0 bits.
|
||||
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Operand satValue;
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
// Negative values always saturate (to zero).
|
||||
// So we must always ignore the sign bit when masking, so that the truncated value will differ from the original one.
|
||||
|
||||
satValue = context.BitwiseAnd(value, Const((int)(uint.MaxValue >> (32 - (int)saturateTo))));
|
||||
}
|
||||
else
|
||||
{
|
||||
satValue = context.ShiftLeft(value, Const(32 - (int)saturateTo));
|
||||
satValue = context.ShiftRightSI(satValue, Const(32 - (int)saturateTo));
|
||||
}
|
||||
|
||||
// If the result is 0, the values are equal and we don't need saturation.
|
||||
Operand lblNoSat = Label();
|
||||
context.BranchIfFalse(lblNoSat, context.Subtract(value, satValue));
|
||||
|
||||
// Saturate and set Q flag.
|
||||
if (unsigned)
|
||||
{
|
||||
if (saturateTo == 31)
|
||||
{
|
||||
// Only saturation case possible when going from 32 bits signed to 32 or 31 bits unsigned
|
||||
// is when the signed input is negative, as all positive values are representable on a 31 bits range.
|
||||
|
||||
satValue = Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
satValue = context.ShiftRightSI(value, Const(31));
|
||||
satValue = context.BitwiseNot(satValue);
|
||||
satValue = context.ShiftRightUI(satValue, Const(32 - (int)saturateTo));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (saturateTo == 1)
|
||||
{
|
||||
satValue = context.ShiftRightSI(value, Const(31));
|
||||
}
|
||||
else
|
||||
{
|
||||
satValue = Const(uint.MaxValue >> (33 - (int)saturateTo));
|
||||
satValue = context.BitwiseExclusiveOr(satValue, context.ShiftRightSI(value, Const(31)));
|
||||
}
|
||||
}
|
||||
|
||||
if (setQ)
|
||||
{
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
}
|
||||
|
||||
context.Copy(result, satValue);
|
||||
|
||||
Operand lblExit = Label();
|
||||
context.Branch(lblExit);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||
{
|
||||
Debug.Assert(saturateTo <= 32);
|
||||
|
||||
if (saturateTo == 32)
|
||||
{
|
||||
// No saturation possible for this case.
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (saturateTo == 0)
|
||||
{
|
||||
// Result is always zero if we saturate 0 bits.
|
||||
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If the result is 0, the values are equal and we don't need saturation.
|
||||
Operand lblNoSat = Label();
|
||||
context.BranchIfFalse(lblNoSat, context.ShiftRightUI(value, Const((int)saturateTo)));
|
||||
|
||||
// Saturate.
|
||||
context.Copy(result, Const(uint.MaxValue >> (32 - (int)saturateTo)));
|
||||
|
||||
Operand lblExit = Label();
|
||||
context.Branch(lblExit);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
private static void EmitSaturateUqsub(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||
{
|
||||
Debug.Assert(saturateTo <= 32);
|
||||
|
||||
if (saturateTo == 32)
|
||||
{
|
||||
// No saturation possible for this case.
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (saturateTo == 0)
|
||||
{
|
||||
// Result is always zero if we saturate 0 bits.
|
||||
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If the result is 0, the values are equal and we don't need saturation.
|
||||
Operand lblNoSat = Label();
|
||||
context.BranchIf(lblNoSat, value, Const(0), Comparison.GreaterOrEqual);
|
||||
|
||||
// Saturate.
|
||||
// Assumes that the value can only underflow, since this is only used for unsigned subtraction.
|
||||
context.Copy(result, Const(0));
|
||||
|
||||
Operand lblExit = Label();
|
||||
context.Branch(lblExit);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
context.Copy(result, value);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
private static Operand EmitSigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||
|
||||
Operand tempN = context.SignExtend16(OperandType.I32, rn);
|
||||
Operand tempM = context.SignExtend16(OperandType.I32, rm);
|
||||
elementAction(tempD, tempN, tempM);
|
||||
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
|
||||
|
||||
tempN = context.ShiftRightSI(rn, Const(16));
|
||||
tempM = context.ShiftRightSI(rm, Const(16));
|
||||
elementAction(tempD, tempN, tempM);
|
||||
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
|
||||
}
|
||||
|
||||
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||
|
||||
Operand tempN = context.ZeroExtend16(OperandType.I32, rn);
|
||||
Operand tempM = context.ZeroExtend16(OperandType.I32, rm);
|
||||
elementAction(tempD, tempN, tempM);
|
||||
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
|
||||
|
||||
tempN = context.ShiftRightUI(rn, Const(16));
|
||||
tempM = context.ShiftRightUI(rm, Const(16));
|
||||
elementAction(tempD, tempN, tempM);
|
||||
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
|
||||
}
|
||||
|
||||
private static Operand EmitSigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
return Emit8BitPair(context, rn, rm, elementAction, unsigned: false);
|
||||
}
|
||||
|
||||
private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
return Emit8BitPair(context, rn, rm, elementAction, unsigned: true);
|
||||
}
|
||||
|
||||
private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction, bool unsigned)
|
||||
{
|
||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||
Operand result = default;
|
||||
|
||||
for (int b = 0; b < 4; b++)
|
||||
{
|
||||
Operand nByte = b != 0 ? context.ShiftRightUI(rn, Const(b * 8)) : rn;
|
||||
Operand mByte = b != 0 ? context.ShiftRightUI(rm, Const(b * 8)) : rm;
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
nByte = context.ZeroExtend8(OperandType.I32, nByte);
|
||||
mByte = context.ZeroExtend8(OperandType.I32, mByte);
|
||||
}
|
||||
else
|
||||
{
|
||||
nByte = context.SignExtend8(OperandType.I32, nByte);
|
||||
mByte = context.SignExtend8(OperandType.I32, mByte);
|
||||
}
|
||||
|
||||
elementAction(tempD, nByte, mByte);
|
||||
|
||||
if (b == 0)
|
||||
{
|
||||
result = context.ZeroExtend8(OperandType.I32, tempD);
|
||||
}
|
||||
else if (b < 3)
|
||||
{
|
||||
result = context.BitwiseOr(result, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, tempD), Const(b * 8)));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = context.BitwiseOr(result, context.ShiftLeft(tempD, Const(24)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
@ -157,7 +157,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.Copy(temp, value);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
if (!context.Memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
@ -198,7 +198,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
SetInt(context, rt, value);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
if (!context.Memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
@ -265,7 +265,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.Copy(GetVec(rt), value);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
if (!context.Memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
@ -312,7 +312,7 @@ namespace ARMeilleure.Instructions
|
||||
break;
|
||||
}
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
if (!context.Memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
@ -385,7 +385,7 @@ namespace ARMeilleure.Instructions
|
||||
break;
|
||||
}
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
if (!context.Memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
@ -403,6 +403,27 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
return EmitHostMappedPointer(context, address);
|
||||
}
|
||||
else if (context.Memory.Type.IsHostTracked())
|
||||
{
|
||||
if (address.Type == OperandType.I32)
|
||||
{
|
||||
address = context.ZeroExtend32(OperandType.I64, address);
|
||||
}
|
||||
|
||||
if (context.Memory.Type == MemoryManagerType.HostTracked)
|
||||
{
|
||||
Operand mask = Const(ulong.MaxValue >> (64 - context.Memory.AddressSpaceBits));
|
||||
address = context.BitwiseAnd(address, mask);
|
||||
}
|
||||
|
||||
Operand ptBase = !context.HasPtc
|
||||
? Const(context.Memory.PageTablePointer.ToInt64())
|
||||
: Const(context.Memory.PageTablePointer.ToInt64(), Ptc.PageTableSymbol);
|
||||
|
||||
Operand ptOffset = context.ShiftRightUI(address, Const(PageBits));
|
||||
|
||||
return context.Add(address, context.Load(OperandType.I64, context.Add(ptBase, context.ShiftLeft(ptOffset, Const(3)))));
|
||||
}
|
||||
|
||||
int ptLevelBits = context.Memory.AddressSpaceBits - PageBits;
|
||||
int ptLevelSize = 1 << ptLevelBits;
|
||||
|
@ -2426,7 +2426,11 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
|
||||
{
|
||||
Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtss, GetVec(op.Rn)), scalar: true);
|
||||
// RSQRTSS handles subnormals as zero, which differs from Arm, so we can't use it here.
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Sqrtss, GetVec(op.Rn));
|
||||
res = context.AddIntrinsic(Intrinsic.X86Rcpss, res);
|
||||
res = EmitSse41Round32Exp8OpF(context, res, scalar: true);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
||||
}
|
||||
@ -2451,7 +2455,11 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
|
||||
{
|
||||
Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtps, GetVec(op.Rn)), scalar: false);
|
||||
// RSQRTPS handles subnormals as zero, which differs from Arm, so we can't use it here.
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Sqrtps, GetVec(op.Rn));
|
||||
res = context.AddIntrinsic(Intrinsic.X86Rcpps, res);
|
||||
res = EmitSse41Round32Exp8OpF(context, res, scalar: false);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
|
@ -1246,6 +1246,33 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
||||
}
|
||||
|
||||
public static void Vqrdmulh(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
EmitVectorBinaryOpI32(context, (op1, op2) =>
|
||||
{
|
||||
if (op.Size == 2)
|
||||
{
|
||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||
op2 = context.SignExtend32(OperandType.I64, op2);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(op1, op2);
|
||||
res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
|
||||
res = context.ShiftRightSI(res, Const(eSize - 1));
|
||||
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
res = context.ConvertI64ToI32(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}, signed: true);
|
||||
}
|
||||
|
||||
public static void Vqsub(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
@ -191,6 +191,26 @@ namespace ARMeilleure.Instructions
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vswp(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.Q)
|
||||
{
|
||||
Operand temp = context.Copy(GetVecA32(op.Qd));
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
|
||||
context.Copy(GetVecA32(op.Qm), temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
|
||||
|
||||
InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
|
||||
InsertScalar(context, op.Vm, temp);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vtbl(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
||||
|
@ -106,6 +106,38 @@ namespace ARMeilleure.Instructions
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vshll2(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
if (op.U)
|
||||
{
|
||||
me = context.ZeroExtend32(OperandType.I64, me);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = context.SignExtend32(OperandType.I64, me);
|
||||
}
|
||||
}
|
||||
|
||||
me = context.ShiftLeft(me, Const(8 << op.Size));
|
||||
|
||||
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vshr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
@ -130,6 +162,36 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
||||
}
|
||||
|
||||
public static void Vsli_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = op.Shift;
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
|
||||
|
||||
Operand res = GetVec(op.Qd);
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
|
||||
|
||||
Operand neShifted = context.ShiftLeft(me, Const(shift));
|
||||
|
||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
|
||||
|
||||
Operand deMasked = context.BitwiseAnd(de, Const(mask));
|
||||
|
||||
Operand e = context.BitwiseOr(neShifted, deMasked);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vsra(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
@ -527,6 +527,7 @@ namespace ARMeilleure.Instructions
|
||||
Pld,
|
||||
Pop,
|
||||
Push,
|
||||
Qadd16,
|
||||
Rev,
|
||||
Revsh,
|
||||
Rsb,
|
||||
@ -571,6 +572,10 @@ namespace ARMeilleure.Instructions
|
||||
Umaal,
|
||||
Umlal,
|
||||
Umull,
|
||||
Uqadd16,
|
||||
Uqadd8,
|
||||
Uqsub16,
|
||||
Uqsub8,
|
||||
Usat,
|
||||
Usat16,
|
||||
Usub8,
|
||||
@ -645,6 +650,7 @@ namespace ARMeilleure.Instructions
|
||||
Vqdmulh,
|
||||
Vqmovn,
|
||||
Vqmovun,
|
||||
Vqrdmulh,
|
||||
Vqrshrn,
|
||||
Vqrshrun,
|
||||
Vqshrn,
|
||||
@ -666,6 +672,7 @@ namespace ARMeilleure.Instructions
|
||||
Vshll,
|
||||
Vshr,
|
||||
Vshrn,
|
||||
Vsli,
|
||||
Vst1,
|
||||
Vst2,
|
||||
Vst3,
|
||||
@ -682,6 +689,7 @@ namespace ARMeilleure.Instructions
|
||||
Vsub,
|
||||
Vsubl,
|
||||
Vsubw,
|
||||
Vswp,
|
||||
Vtbl,
|
||||
Vtrn,
|
||||
Vtst,
|
||||
|
@ -91,54 +91,54 @@ namespace ARMeilleure.Instructions
|
||||
#region "Read"
|
||||
public static byte ReadByte(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<byte>(address);
|
||||
return GetMemoryManager().ReadGuest<byte>(address);
|
||||
}
|
||||
|
||||
public static ushort ReadUInt16(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<ushort>(address);
|
||||
return GetMemoryManager().ReadGuest<ushort>(address);
|
||||
}
|
||||
|
||||
public static uint ReadUInt32(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<uint>(address);
|
||||
return GetMemoryManager().ReadGuest<uint>(address);
|
||||
}
|
||||
|
||||
public static ulong ReadUInt64(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<ulong>(address);
|
||||
return GetMemoryManager().ReadGuest<ulong>(address);
|
||||
}
|
||||
|
||||
public static V128 ReadVector128(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<V128>(address);
|
||||
return GetMemoryManager().ReadGuest<V128>(address);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Write"
|
||||
public static void WriteByte(ulong address, byte value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt16(ulong address, ushort value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt32(ulong address, uint value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt64(ulong address, ulong value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
public static void WriteVector128(ulong address, V128 value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -28,6 +28,17 @@ namespace ARMeilleure.Memory
|
||||
/// <returns>The data</returns>
|
||||
T ReadTracked<T>(ulong va) where T : unmanaged;
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from CPU mapped memory, from guest code. (with read tracking)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the data being read</typeparam>
|
||||
/// <param name="va">Virtual address of the data in memory</param>
|
||||
/// <returns>The data</returns>
|
||||
T ReadGuest<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
return ReadTracked<T>(va);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to CPU mapped memory.
|
||||
/// </summary>
|
||||
@ -36,6 +47,17 @@ namespace ARMeilleure.Memory
|
||||
/// <param name="value">Data to be written</param>
|
||||
void Write<T>(ulong va, T value) where T : unmanaged;
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to CPU mapped memory, from guest code.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the data being written</typeparam>
|
||||
/// <param name="va">Virtual address to write the data into</param>
|
||||
/// <param name="value">Data to be written</param>
|
||||
void WriteGuest<T>(ulong va, T value) where T : unmanaged
|
||||
{
|
||||
Write(va, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only span of data from CPU mapped memory.
|
||||
/// </summary>
|
||||
|
@ -29,6 +29,18 @@ namespace ARMeilleure.Memory
|
||||
/// Allows invalid access from JIT code to the rest of the program, but is faster.
|
||||
/// </summary>
|
||||
HostMappedUnsafe,
|
||||
|
||||
/// <summary>
|
||||
/// High level implementation using a software flat page table for address translation
|
||||
/// with no support for handling invalid or non-contiguous memory access.
|
||||
/// </summary>
|
||||
HostTracked,
|
||||
|
||||
/// <summary>
|
||||
/// High level implementation using a software flat page table for address translation
|
||||
/// without masking the address and no support for handling invalid or non-contiguous memory access.
|
||||
/// </summary>
|
||||
HostTrackedUnsafe,
|
||||
}
|
||||
|
||||
public static class MemoryManagerTypeExtensions
|
||||
@ -37,5 +49,15 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
||||
}
|
||||
|
||||
public static bool IsHostTracked(this MemoryManagerType type)
|
||||
{
|
||||
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostTrackedUnsafe;
|
||||
}
|
||||
|
||||
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
||||
{
|
||||
return type.IsHostMapped() || type.IsHostTracked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,8 @@ namespace ARMeilleure.Signal
|
||||
|
||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
||||
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize, ulong pageSize)
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize)
|
||||
{
|
||||
ulong pageMask = pageSize - 1;
|
||||
|
||||
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
||||
context.Copy(inRegionLocal, Const(0));
|
||||
|
||||
@ -51,7 +49,7 @@ namespace ARMeilleure.Signal
|
||||
// Only call tracking if in range.
|
||||
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
||||
|
||||
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~pageMask));
|
||||
Operand offset = context.Subtract(faultAddress, rangeAddress);
|
||||
|
||||
// Call the tracking action, with the pointer's relative offset to the base address.
|
||||
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
|
||||
@ -62,8 +60,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
// Tracking action should be non-null to call it, otherwise assume false return.
|
||||
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
||||
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(pageSize), isWrite);
|
||||
context.Copy(inRegionLocal, result);
|
||||
Operand result = context.Call(trackingActionPtr, OperandType.I64, offset, Const(1UL), isWrite);
|
||||
context.Copy(inRegionLocal, context.ICompareNotEqual(result, Const(0UL)));
|
||||
|
||||
GenerateFaultAddressPatchCode(context, faultAddress, result);
|
||||
|
||||
context.MarkLabel(skipActionLabel);
|
||||
|
||||
@ -155,7 +155,7 @@ namespace ARMeilleure.Signal
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@ -168,7 +168,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize);
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
@ -203,7 +203,7 @@ namespace ARMeilleure.Signal
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||
}
|
||||
|
||||
public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||
public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@ -232,7 +232,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize);
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
@ -256,5 +256,86 @@ namespace ARMeilleure.Signal
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||
}
|
||||
|
||||
private static void GenerateFaultAddressPatchCode(EmitterContext context, Operand faultAddress, Operand newAddress)
|
||||
{
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
if (SupportsFaultAddressPatchingForHostOs())
|
||||
{
|
||||
Operand lblSkip = Label();
|
||||
|
||||
context.BranchIf(lblSkip, faultAddress, newAddress, Comparison.Equal);
|
||||
|
||||
Operand ucontextPtr = context.LoadArgument(OperandType.I64, 2);
|
||||
Operand pcCtxAddress = default;
|
||||
ulong baseRegsOffset = 0;
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
pcCtxAddress = context.Add(ucontextPtr, Const(440UL));
|
||||
baseRegsOffset = 184UL;
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
{
|
||||
ucontextPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(48UL)));
|
||||
|
||||
pcCtxAddress = context.Add(ucontextPtr, Const(272UL));
|
||||
baseRegsOffset = 16UL;
|
||||
}
|
||||
|
||||
Operand pc = context.Load(OperandType.I64, pcCtxAddress);
|
||||
|
||||
Operand reg = GetAddressRegisterFromArm64Instruction(context, pc);
|
||||
Operand reg64 = context.ZeroExtend32(OperandType.I64, reg);
|
||||
Operand regCtxAddress = context.Add(ucontextPtr, context.Add(context.ShiftLeft(reg64, Const(3)), Const(baseRegsOffset)));
|
||||
Operand regAddress = context.Load(OperandType.I64, regCtxAddress);
|
||||
|
||||
Operand addressDelta = context.Subtract(regAddress, faultAddress);
|
||||
|
||||
context.Store(regCtxAddress, context.Add(newAddress, addressDelta));
|
||||
|
||||
context.MarkLabel(lblSkip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetAddressRegisterFromArm64Instruction(EmitterContext context, Operand pc)
|
||||
{
|
||||
Operand inst = context.Load(OperandType.I32, pc);
|
||||
Operand reg = context.AllocateLocal(OperandType.I32);
|
||||
|
||||
Operand isSysInst = context.ICompareEqual(context.BitwiseAnd(inst, Const(0xFFF80000)), Const(0xD5080000));
|
||||
|
||||
Operand lblSys = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblSys, isSysInst, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Copy(reg, context.BitwiseAnd(context.ShiftRightUI(inst, Const(5)), Const(0x1F)));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSys);
|
||||
context.Copy(reg, context.BitwiseAnd(inst, Const(0x1F)));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
public static bool SupportsFaultAddressPatchingForHost()
|
||||
{
|
||||
return SupportsFaultAddressPatchingForHostArch() && SupportsFaultAddressPatchingForHostOs();
|
||||
}
|
||||
|
||||
private static bool SupportsFaultAddressPatchingForHostArch()
|
||||
{
|
||||
return RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
||||
}
|
||||
|
||||
private static bool SupportsFaultAddressPatchingForHostOs()
|
||||
{
|
||||
return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
|
||||
private int[] _postOrderMap;
|
||||
|
||||
public int LocalsCount { get; private set; }
|
||||
public BasicBlock Entry { get; }
|
||||
public BasicBlock Entry { get; private set; }
|
||||
public IntrusiveList<BasicBlock> Blocks { get; }
|
||||
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
||||
public int[] PostOrderMap => _postOrderMap;
|
||||
@ -34,6 +34,15 @@ namespace ARMeilleure.Translation
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateEntry(BasicBlock newEntry)
|
||||
{
|
||||
newEntry.AddSuccessor(Entry);
|
||||
|
||||
Entry = newEntry;
|
||||
Blocks.AddFirst(newEntry);
|
||||
Update();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
RemoveUnreachableBlocks(Blocks);
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
@ -11,11 +10,10 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public IntPtr FuncPtr { get; }
|
||||
|
||||
public DelegateInfo(Delegate dlg)
|
||||
public DelegateInfo(Delegate dlg, IntPtr funcPtr)
|
||||
{
|
||||
_dlg = dlg;
|
||||
|
||||
FuncPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(dlg);
|
||||
FuncPtr = funcPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
@ -64,11 +65,11 @@ namespace ARMeilleure.Translation
|
||||
return index;
|
||||
}
|
||||
|
||||
private static void SetDelegateInfo(Delegate dlg)
|
||||
private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr)
|
||||
{
|
||||
string key = GetKey(dlg.Method);
|
||||
|
||||
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
||||
_delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
|
||||
}
|
||||
|
||||
private static string GetKey(MethodInfo info)
|
||||
@ -82,179 +83,353 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
_delegates = new SortedList<string, DelegateInfo>();
|
||||
|
||||
SetDelegateInfo(new MathAbs(Math.Abs));
|
||||
SetDelegateInfo(new MathCeiling(Math.Ceiling));
|
||||
SetDelegateInfo(new MathFloor(Math.Floor));
|
||||
SetDelegateInfo(new MathRound(Math.Round));
|
||||
SetDelegateInfo(new MathTruncate(Math.Truncate));
|
||||
var dlgMathAbs = new MathAbs(Math.Abs);
|
||||
var dlgMathCeiling = new MathCeiling(Math.Ceiling);
|
||||
var dlgMathFloor = new MathFloor(Math.Floor);
|
||||
var dlgMathRound = new MathRound(Math.Round);
|
||||
var dlgMathTruncate = new MathTruncate(Math.Truncate);
|
||||
|
||||
SetDelegateInfo(new MathFAbs(MathF.Abs));
|
||||
SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
|
||||
SetDelegateInfo(new MathFFloor(MathF.Floor));
|
||||
SetDelegateInfo(new MathFRound(MathF.Round));
|
||||
SetDelegateInfo(new MathFTruncate(MathF.Truncate));
|
||||
var dlgMathFAbs = new MathFAbs(MathF.Abs);
|
||||
var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling);
|
||||
var dlgMathFFloor = new MathFFloor(MathF.Floor);
|
||||
var dlgMathFRound = new MathFRound(MathF.Round);
|
||||
var dlgMathFTruncate = new MathFTruncate(MathF.Truncate);
|
||||
|
||||
SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
|
||||
SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
|
||||
SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
|
||||
SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
|
||||
SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
|
||||
SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
|
||||
SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
|
||||
SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
|
||||
SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
|
||||
SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
|
||||
SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
|
||||
SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
|
||||
SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
|
||||
SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
|
||||
SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
|
||||
SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
|
||||
SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
|
||||
SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
|
||||
SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
|
||||
SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
|
||||
SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
|
||||
SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
|
||||
SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
|
||||
SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
|
||||
var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break);
|
||||
var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization);
|
||||
var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit);
|
||||
var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0);
|
||||
var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0);
|
||||
var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0);
|
||||
var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0);
|
||||
var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0);
|
||||
var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress);
|
||||
var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine);
|
||||
var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte);
|
||||
var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16);
|
||||
var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32);
|
||||
var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64);
|
||||
var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128);
|
||||
var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking);
|
||||
var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall);
|
||||
var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess);
|
||||
var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined);
|
||||
var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte);
|
||||
var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16);
|
||||
var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32);
|
||||
var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64);
|
||||
var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128);
|
||||
|
||||
SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
|
||||
SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
|
||||
SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
|
||||
SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
|
||||
SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
|
||||
SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
|
||||
SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
|
||||
SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
|
||||
SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
|
||||
SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
|
||||
SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
|
||||
SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
|
||||
SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
|
||||
SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
|
||||
SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
|
||||
SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
|
||||
SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
|
||||
SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
|
||||
SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
|
||||
SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
|
||||
SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
|
||||
SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
|
||||
SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
|
||||
SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
|
||||
SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
|
||||
SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
|
||||
SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
|
||||
SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
|
||||
SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
|
||||
SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
|
||||
SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
|
||||
SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
|
||||
SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
|
||||
SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
|
||||
SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
|
||||
SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
|
||||
SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
|
||||
SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
|
||||
SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
|
||||
SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
|
||||
SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
|
||||
SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
|
||||
SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
|
||||
var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns);
|
||||
var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros);
|
||||
var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b);
|
||||
var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb);
|
||||
var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch);
|
||||
var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw);
|
||||
var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx);
|
||||
var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h);
|
||||
var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w);
|
||||
var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x);
|
||||
var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt);
|
||||
var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt);
|
||||
var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate);
|
||||
var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose);
|
||||
var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower);
|
||||
var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority);
|
||||
var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity);
|
||||
var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper);
|
||||
var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns);
|
||||
var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns);
|
||||
var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128);
|
||||
var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32);
|
||||
var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64);
|
||||
var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32);
|
||||
var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64);
|
||||
var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32);
|
||||
var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64);
|
||||
var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32);
|
||||
var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64);
|
||||
var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1);
|
||||
var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2);
|
||||
var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1);
|
||||
var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2);
|
||||
var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64);
|
||||
var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1);
|
||||
var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2);
|
||||
var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3);
|
||||
var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4);
|
||||
var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1);
|
||||
var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2);
|
||||
var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3);
|
||||
var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4);
|
||||
var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64);
|
||||
|
||||
SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
|
||||
SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
|
||||
var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert);
|
||||
var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert);
|
||||
|
||||
SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
|
||||
SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
|
||||
SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
|
||||
SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
|
||||
SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
|
||||
SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
|
||||
SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
|
||||
SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
|
||||
SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
|
||||
SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
|
||||
SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
|
||||
SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
|
||||
SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
|
||||
SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
|
||||
SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
|
||||
SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
|
||||
SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
|
||||
SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
|
||||
SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
|
||||
SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
|
||||
SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
|
||||
SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
|
||||
SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
|
||||
SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
|
||||
SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
|
||||
var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd);
|
||||
var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare);
|
||||
var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ);
|
||||
var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE);
|
||||
var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT);
|
||||
var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE);
|
||||
var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT);
|
||||
var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv);
|
||||
var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax);
|
||||
var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum);
|
||||
var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin);
|
||||
var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum);
|
||||
var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul);
|
||||
var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd);
|
||||
var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub);
|
||||
var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX);
|
||||
var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd);
|
||||
var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub);
|
||||
var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate);
|
||||
var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only.
|
||||
var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused);
|
||||
var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX);
|
||||
var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate);
|
||||
var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
|
||||
var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only.
|
||||
var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused);
|
||||
var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt);
|
||||
var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub);
|
||||
|
||||
SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
|
||||
var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert);
|
||||
|
||||
SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
|
||||
SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
|
||||
SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
|
||||
SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
|
||||
SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
|
||||
SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
|
||||
SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
|
||||
SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
|
||||
SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
|
||||
SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
|
||||
SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
|
||||
SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
|
||||
SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
|
||||
SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
|
||||
SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
|
||||
SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
|
||||
SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
|
||||
SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
|
||||
SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
|
||||
SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
|
||||
SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
|
||||
SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
|
||||
SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
|
||||
SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
|
||||
SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
|
||||
SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
|
||||
var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd);
|
||||
var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare);
|
||||
var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ);
|
||||
var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE);
|
||||
var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT);
|
||||
var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE);
|
||||
var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT);
|
||||
var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv);
|
||||
var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax);
|
||||
var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum);
|
||||
var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin);
|
||||
var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum);
|
||||
var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul);
|
||||
var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd);
|
||||
var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub);
|
||||
var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX);
|
||||
var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd);
|
||||
var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub);
|
||||
var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate);
|
||||
var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only.
|
||||
var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused);
|
||||
var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX);
|
||||
var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate);
|
||||
var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
|
||||
var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only.
|
||||
var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused);
|
||||
var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt);
|
||||
var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub);
|
||||
|
||||
SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert));
|
||||
var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert);
|
||||
|
||||
SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate<MathAbs>(dlgMathAbs));
|
||||
SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate<MathCeiling>(dlgMathCeiling));
|
||||
SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate<MathFloor>(dlgMathFloor));
|
||||
SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate<MathRound>(dlgMathRound));
|
||||
SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate<MathTruncate>(dlgMathTruncate));
|
||||
|
||||
SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate<MathFAbs>(dlgMathFAbs));
|
||||
SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate<MathFCeiling>(dlgMathFCeiling));
|
||||
SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate<MathFFloor>(dlgMathFFloor));
|
||||
SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate<MathFRound>(dlgMathFRound));
|
||||
SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate<MathFTruncate>(dlgMathFTruncate));
|
||||
|
||||
SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate<NativeInterfaceBreak>(dlgNativeInterfaceBreak));
|
||||
SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate<NativeInterfaceCheckSynchronization>(dlgNativeInterfaceCheckSynchronization));
|
||||
SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate<NativeInterfaceEnqueueForRejit>(dlgNativeInterfaceEnqueueForRejit));
|
||||
SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntfrqEl0>(dlgNativeInterfaceGetCntfrqEl0));
|
||||
SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntpctEl0>(dlgNativeInterfaceGetCntpctEl0));
|
||||
SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntvctEl0>(dlgNativeInterfaceGetCntvctEl0));
|
||||
SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCtrEl0>(dlgNativeInterfaceGetCtrEl0));
|
||||
SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetDczidEl0>(dlgNativeInterfaceGetDczidEl0));
|
||||
SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetFunctionAddress>(dlgNativeInterfaceGetFunctionAddress));
|
||||
SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate<NativeInterfaceInvalidateCacheLine>(dlgNativeInterfaceInvalidateCacheLine));
|
||||
SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadByte>(dlgNativeInterfaceReadByte));
|
||||
SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt16>(dlgNativeInterfaceReadUInt16));
|
||||
SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt32>(dlgNativeInterfaceReadUInt32));
|
||||
SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt64>(dlgNativeInterfaceReadUInt64));
|
||||
SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadVector128>(dlgNativeInterfaceReadVector128));
|
||||
SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSignalMemoryTracking>(dlgNativeInterfaceSignalMemoryTracking));
|
||||
SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSupervisorCall>(dlgNativeInterfaceSupervisorCall));
|
||||
SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate<NativeInterfaceThrowInvalidMemoryAccess>(dlgNativeInterfaceThrowInvalidMemoryAccess));
|
||||
SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate<NativeInterfaceUndefined>(dlgNativeInterfaceUndefined));
|
||||
SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteByte>(dlgNativeInterfaceWriteByte));
|
||||
SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt16>(dlgNativeInterfaceWriteUInt16));
|
||||
SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt32>(dlgNativeInterfaceWriteUInt32));
|
||||
SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt64>(dlgNativeInterfaceWriteUInt64));
|
||||
SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteVector128>(dlgNativeInterfaceWriteVector128));
|
||||
|
||||
SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingSigns>(dlgSoftFallbackCountLeadingSigns));
|
||||
SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingZeros>(dlgSoftFallbackCountLeadingZeros));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32b>(dlgSoftFallbackCrc32b));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cb>(dlgSoftFallbackCrc32cb));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32ch>(dlgSoftFallbackCrc32ch));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cw>(dlgSoftFallbackCrc32cw));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cx>(dlgSoftFallbackCrc32cx));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32h>(dlgSoftFallbackCrc32h));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32w>(dlgSoftFallbackCrc32w));
|
||||
SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32x>(dlgSoftFallbackCrc32x));
|
||||
SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackDecrypt>(dlgSoftFallbackDecrypt));
|
||||
SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackEncrypt>(dlgSoftFallbackEncrypt));
|
||||
SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate<SoftFallbackFixedRotate>(dlgSoftFallbackFixedRotate));
|
||||
SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashChoose>(dlgSoftFallbackHashChoose));
|
||||
SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashLower>(dlgSoftFallbackHashLower));
|
||||
SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashMajority>(dlgSoftFallbackHashMajority));
|
||||
SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashParity>(dlgSoftFallbackHashParity));
|
||||
SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashUpper>(dlgSoftFallbackHashUpper));
|
||||
SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackInverseMixColumns>(dlgSoftFallbackInverseMixColumns));
|
||||
SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackMixColumns>(dlgSoftFallbackMixColumns));
|
||||
SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate<SoftFallbackPolynomialMult64_128>(dlgSoftFallbackPolynomialMult64_128));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS32>(dlgSoftFallbackSatF32ToS32));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS64>(dlgSoftFallbackSatF32ToS64));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU32>(dlgSoftFallbackSatF32ToU32));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU64>(dlgSoftFallbackSatF32ToU64));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS32>(dlgSoftFallbackSatF64ToS32));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS64>(dlgSoftFallbackSatF64ToS64));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU32>(dlgSoftFallbackSatF64ToU32));
|
||||
SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU64>(dlgSoftFallbackSatF64ToU64));
|
||||
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart1>(dlgSoftFallbackSha1SchedulePart1));
|
||||
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart2>(dlgSoftFallbackSha1SchedulePart2));
|
||||
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart1>(dlgSoftFallbackSha256SchedulePart1));
|
||||
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart2>(dlgSoftFallbackSha256SchedulePart2));
|
||||
SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSignedShrImm64>(dlgSoftFallbackSignedShrImm64));
|
||||
SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl1>(dlgSoftFallbackTbl1));
|
||||
SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl2>(dlgSoftFallbackTbl2));
|
||||
SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl3>(dlgSoftFallbackTbl3));
|
||||
SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl4>(dlgSoftFallbackTbl4));
|
||||
SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx1>(dlgSoftFallbackTbx1));
|
||||
SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx2>(dlgSoftFallbackTbx2));
|
||||
SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx3>(dlgSoftFallbackTbx3));
|
||||
SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx4>(dlgSoftFallbackTbx4));
|
||||
SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackUnsignedShrImm64>(dlgSoftFallbackUnsignedShrImm64));
|
||||
|
||||
SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_32FPConvert>(dlgSoftFloat16_32FPConvert));
|
||||
SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_64FPConvert>(dlgSoftFloat16_64FPConvert));
|
||||
|
||||
SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAdd>(dlgSoftFloat32FPAdd));
|
||||
SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAddFpscr>(dlgSoftFloat32FPAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompare>(dlgSoftFloat32FPCompare));
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQ>(dlgSoftFloat32FPCompareEQ));
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQFpscr>(dlgSoftFloat32FPCompareEQFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGE>(dlgSoftFloat32FPCompareGE));
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGEFpscr>(dlgSoftFloat32FPCompareGEFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGT>(dlgSoftFloat32FPCompareGT));
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGTFpscr>(dlgSoftFloat32FPCompareGTFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLE>(dlgSoftFloat32FPCompareLE));
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLEFpscr>(dlgSoftFloat32FPCompareLEFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLT>(dlgSoftFloat32FPCompareLT));
|
||||
SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLTFpscr>(dlgSoftFloat32FPCompareLTFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPDiv>(dlgSoftFloat32FPDiv));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMax>(dlgSoftFloat32FPMax));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxFpscr>(dlgSoftFloat32FPMaxFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNum>(dlgSoftFloat32FPMaxNum));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNumFpscr>(dlgSoftFloat32FPMaxNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMin>(dlgSoftFloat32FPMin));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinFpscr>(dlgSoftFloat32FPMinFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNum>(dlgSoftFloat32FPMinNum));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNumFpscr>(dlgSoftFloat32FPMinNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMul>(dlgSoftFloat32FPMul));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulFpscr>(dlgSoftFloat32FPMulFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAdd>(dlgSoftFloat32FPMulAdd));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAddFpscr>(dlgSoftFloat32FPMulAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSub>(dlgSoftFloat32FPMulSub));
|
||||
SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSubFpscr>(dlgSoftFloat32FPMulSubFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulX>(dlgSoftFloat32FPMulX));
|
||||
SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulAdd>(dlgSoftFloat32FPNegMulAdd));
|
||||
SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulSub>(dlgSoftFloat32FPNegMulSub));
|
||||
SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimate>(dlgSoftFloat32FPRecipEstimate));
|
||||
SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimateFpscr>(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStep>(dlgSoftFloat32FPRecipStep)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStepFused>(dlgSoftFloat32FPRecipStepFused));
|
||||
SetDelegateInfo(dlgSoftFloat32FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecpX>(dlgSoftFloat32FPRecpX));
|
||||
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimate>(dlgSoftFloat32FPRSqrtEstimate));
|
||||
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimateFpscr>(dlgSoftFloat32FPRSqrtEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStep>(dlgSoftFloat32FPRSqrtStep)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat32FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStepFused>(dlgSoftFloat32FPRSqrtStepFused));
|
||||
SetDelegateInfo(dlgSoftFloat32FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSqrt>(dlgSoftFloat32FPSqrt));
|
||||
SetDelegateInfo(dlgSoftFloat32FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSub>(dlgSoftFloat32FPSub));
|
||||
|
||||
SetDelegateInfo(dlgSoftFloat32_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat32_16FPConvert>(dlgSoftFloat32_16FPConvert));
|
||||
|
||||
SetDelegateInfo(dlgSoftFloat64FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAdd>(dlgSoftFloat64FPAdd));
|
||||
SetDelegateInfo(dlgSoftFloat64FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAddFpscr>(dlgSoftFloat64FPAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompare>(dlgSoftFloat64FPCompare));
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQ>(dlgSoftFloat64FPCompareEQ));
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQFpscr>(dlgSoftFloat64FPCompareEQFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGE>(dlgSoftFloat64FPCompareGE));
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGEFpscr>(dlgSoftFloat64FPCompareGEFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGT>(dlgSoftFloat64FPCompareGT));
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGTFpscr>(dlgSoftFloat64FPCompareGTFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLE>(dlgSoftFloat64FPCompareLE));
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLEFpscr>(dlgSoftFloat64FPCompareLEFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLT>(dlgSoftFloat64FPCompareLT));
|
||||
SetDelegateInfo(dlgSoftFloat64FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLTFpscr>(dlgSoftFloat64FPCompareLTFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPDiv>(dlgSoftFloat64FPDiv));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMax>(dlgSoftFloat64FPMax));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxFpscr>(dlgSoftFloat64FPMaxFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNum>(dlgSoftFloat64FPMaxNum));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNumFpscr>(dlgSoftFloat64FPMaxNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMin>(dlgSoftFloat64FPMin));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinFpscr>(dlgSoftFloat64FPMinFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNum>(dlgSoftFloat64FPMinNum));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNumFpscr>(dlgSoftFloat64FPMinNumFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMul>(dlgSoftFloat64FPMul));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulFpscr>(dlgSoftFloat64FPMulFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAdd>(dlgSoftFloat64FPMulAdd));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAddFpscr>(dlgSoftFloat64FPMulAddFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSub>(dlgSoftFloat64FPMulSub));
|
||||
SetDelegateInfo(dlgSoftFloat64FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSubFpscr>(dlgSoftFloat64FPMulSubFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulX>(dlgSoftFloat64FPMulX));
|
||||
SetDelegateInfo(dlgSoftFloat64FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulAdd>(dlgSoftFloat64FPNegMulAdd));
|
||||
SetDelegateInfo(dlgSoftFloat64FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulSub>(dlgSoftFloat64FPNegMulSub));
|
||||
SetDelegateInfo(dlgSoftFloat64FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimate>(dlgSoftFloat64FPRecipEstimate));
|
||||
SetDelegateInfo(dlgSoftFloat64FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimateFpscr>(dlgSoftFloat64FPRecipEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStep>(dlgSoftFloat64FPRecipStep)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStepFused>(dlgSoftFloat64FPRecipStepFused));
|
||||
SetDelegateInfo(dlgSoftFloat64FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecpX>(dlgSoftFloat64FPRecpX));
|
||||
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimate>(dlgSoftFloat64FPRSqrtEstimate));
|
||||
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimateFpscr>(dlgSoftFloat64FPRSqrtEstimateFpscr)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStep>(dlgSoftFloat64FPRSqrtStep)); // A32 only.
|
||||
SetDelegateInfo(dlgSoftFloat64FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStepFused>(dlgSoftFloat64FPRSqrtStepFused));
|
||||
SetDelegateInfo(dlgSoftFloat64FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSqrt>(dlgSoftFloat64FPSqrt));
|
||||
SetDelegateInfo(dlgSoftFloat64FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSub>(dlgSoftFloat64FPSub));
|
||||
|
||||
SetDelegateInfo(dlgSoftFloat64_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat64_16FPConvert>(dlgSoftFloat64_16FPConvert));
|
||||
}
|
||||
|
||||
private delegate double MathAbs(double value);
|
||||
|
@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
@ -857,8 +857,14 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
threads.ForEach((thread) => thread.Start());
|
||||
threads.ForEach((thread) => thread.Join());
|
||||
foreach (var thread in threads)
|
||||
{
|
||||
thread.Start();
|
||||
}
|
||||
foreach (var thread in threads)
|
||||
{
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
threads.Clear();
|
||||
|
||||
|
@ -89,6 +89,17 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
||||
{
|
||||
if (cfg.Entry.Predecessors.Count != 0)
|
||||
{
|
||||
// We expect the entry block to have no predecessors.
|
||||
// This is required because we have a implicit context load at the start of the function,
|
||||
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
||||
// Here we insert a new entry block that will jump to the existing entry block.
|
||||
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
|
||||
|
||||
cfg.UpdateEntry(newEntry);
|
||||
}
|
||||
|
||||
// Compute local register inputs and outputs used inside blocks.
|
||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||
@ -201,7 +212,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
// The only block without any predecessor should be the entry block.
|
||||
// It always needs a context load as it is the first block to run.
|
||||
if (block.Predecessors.Count == 0 || hasContextLoad)
|
||||
if (block == cfg.Entry || hasContextLoad)
|
||||
{
|
||||
long vecMask = globalInputs[block.Index].VecMask;
|
||||
long intMask = globalInputs[block.Index].IntMask;
|
||||
|
@ -80,7 +80,10 @@ namespace ARMeilleure.Translation
|
||||
return true;
|
||||
}
|
||||
|
||||
Monitor.Wait(Sync);
|
||||
if (!_disposed)
|
||||
{
|
||||
Monitor.Wait(Sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,25 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
private bool _stillRunning;
|
||||
private readonly Thread _updaterThread;
|
||||
|
||||
private float _volume;
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
set
|
||||
{
|
||||
_volume = value;
|
||||
|
||||
foreach (OpenALHardwareDeviceSession session in _sessions.Keys)
|
||||
{
|
||||
session.UpdateMasterVolume(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OpenALHardwareDeviceDriver()
|
||||
{
|
||||
_device = ALC.OpenDevice("");
|
||||
@ -34,6 +53,8 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
Name = "HardwareDeviceDriver.OpenAL",
|
||||
};
|
||||
|
||||
_volume = 1f;
|
||||
|
||||
_updaterThread.Start();
|
||||
}
|
||||
|
||||
@ -52,7 +73,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
}
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
{
|
||||
if (channelCount == 0)
|
||||
{
|
||||
@ -73,7 +94,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
throw new ArgumentException($"{channelCount}");
|
||||
}
|
||||
|
||||
OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||
OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
|
||||
_sessions.TryAdd(session, 0);
|
||||
|
||||
|
@ -16,10 +16,11 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
private bool _isActive;
|
||||
private readonly Queue<OpenALAudioBuffer> _queuedBuffers;
|
||||
private ulong _playedSampleCount;
|
||||
private float _volume;
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_queuedBuffers = new Queue<OpenALAudioBuffer>();
|
||||
@ -27,7 +28,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
_targetFormat = GetALFormat();
|
||||
_isActive = false;
|
||||
_playedSampleCount = 0;
|
||||
SetVolume(requestedVolume);
|
||||
SetVolume(1f);
|
||||
}
|
||||
|
||||
private ALFormat GetALFormat()
|
||||
@ -85,17 +86,22 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
|
||||
public override void SetVolume(float volume)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
AL.Source(_sourceId, ALSourcef.Gain, volume);
|
||||
}
|
||||
_volume = volume;
|
||||
|
||||
UpdateMasterVolume(_driver.Volume);
|
||||
}
|
||||
|
||||
public override float GetVolume()
|
||||
{
|
||||
AL.GetSource(_sourceId, ALSourcef.Gain, out float volume);
|
||||
return _volume;
|
||||
}
|
||||
|
||||
return volume;
|
||||
public void UpdateMasterVolume(float newVolume)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
AL.Source(_sourceId, ALSourcef.Gain, newVolume * _volume);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
|
@ -20,6 +20,8 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
private readonly bool _supportSurroundConfiguration;
|
||||
|
||||
public float Volume { get; set; }
|
||||
|
||||
// TODO: Add this to SDL2-CS
|
||||
// NOTE: We use a DllImport here because of marshaling issue for spec.
|
||||
#pragma warning disable SYSLIB1054
|
||||
@ -48,6 +50,8 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
_supportSurroundConfiguration = spec.channels >= 6;
|
||||
}
|
||||
|
||||
Volume = 1f;
|
||||
}
|
||||
|
||||
public static bool IsSupported => IsSupportedInternal();
|
||||
@ -74,7 +78,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
return _pauseEvent;
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
{
|
||||
if (channelCount == 0)
|
||||
{
|
||||
@ -91,7 +95,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
||||
}
|
||||
|
||||
SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||
SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
|
||||
_sessions.TryAdd(session, 0);
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
@ -26,7 +28,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
private float _volume;
|
||||
private readonly ushort _nativeSampleFormat;
|
||||
|
||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||
@ -37,7 +39,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
||||
_sampleCount = uint.MaxValue;
|
||||
_started = false;
|
||||
_volume = requestedVolume;
|
||||
_volume = 1f;
|
||||
}
|
||||
|
||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||
@ -87,7 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] samples = new byte[frameCount * _bytesPerFrame];
|
||||
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * _bytesPerFrame);
|
||||
|
||||
Span<byte> samples = samplesOwner.Span;
|
||||
|
||||
_ringBuffer.Read(samples, 0, samples.Length);
|
||||
|
||||
@ -99,7 +103,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
streamSpan.Clear();
|
||||
|
||||
// Apply volume to written data
|
||||
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
|
||||
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_driver.Volume * _volume * SDL_MIX_MAXVOLUME));
|
||||
}
|
||||
|
||||
ulong sampleCount = GetSampleCount(samples.Length);
|
||||
|
@ -19,6 +19,25 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
||||
private int _disposeState;
|
||||
|
||||
private float _volume = 1f;
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
set
|
||||
{
|
||||
_volume = value;
|
||||
|
||||
foreach (SoundIoHardwareDeviceSession session in _sessions.Keys)
|
||||
{
|
||||
session.UpdateMasterVolume(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SoundIoHardwareDeviceDriver()
|
||||
{
|
||||
_audioContext = SoundIoContext.Create();
|
||||
@ -122,7 +141,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
return _pauseEvent;
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
{
|
||||
if (channelCount == 0)
|
||||
{
|
||||
@ -134,14 +153,12 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
sampleRate = Constants.TargetSampleRate;
|
||||
}
|
||||
|
||||
volume = Math.Clamp(volume, 0, 1);
|
||||
|
||||
if (direction != Direction.Output)
|
||||
{
|
||||
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
||||
}
|
||||
|
||||
SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||
SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
|
||||
_sessions.TryAdd(session, 0);
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Backends.SoundIo.Native;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
@ -18,16 +20,18 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
private readonly DynamicRingBuffer _ringBuffer;
|
||||
private ulong _playedSampleCount;
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private float _volume;
|
||||
private int _disposeState;
|
||||
|
||||
public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
|
||||
_ringBuffer = new DynamicRingBuffer();
|
||||
_volume = 1f;
|
||||
|
||||
SetupOutputStream(requestedVolume);
|
||||
SetupOutputStream(driver.Volume);
|
||||
}
|
||||
|
||||
private void SetupOutputStream(float requestedVolume)
|
||||
@ -35,7 +39,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
_outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
|
||||
_outputStream.WriteCallback += Update;
|
||||
_outputStream.Volume = requestedVolume;
|
||||
// TODO: Setup other callbacks (errors, ect).
|
||||
// TODO: Setup other callbacks (errors, etc.)
|
||||
|
||||
_outputStream.Open();
|
||||
}
|
||||
@ -47,7 +51,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
|
||||
public override float GetVolume()
|
||||
{
|
||||
return _outputStream.Volume;
|
||||
return _volume;
|
||||
}
|
||||
|
||||
public override void PrepareToClose() { }
|
||||
@ -63,7 +67,14 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
|
||||
public override void SetVolume(float volume)
|
||||
{
|
||||
_outputStream.SetVolume(volume);
|
||||
_volume = volume;
|
||||
|
||||
_outputStream.SetVolume(_driver.Volume * volume);
|
||||
}
|
||||
|
||||
public void UpdateMasterVolume(float newVolume)
|
||||
{
|
||||
_outputStream.SetVolume(newVolume * _volume);
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
@ -111,7 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
|
||||
int channelCount = areas.Length;
|
||||
|
||||
byte[] samples = new byte[frameCount * bytesPerFrame];
|
||||
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * bytesPerFrame);
|
||||
|
||||
Span<byte> samples = samplesOwner.Span;
|
||||
|
||||
_ringBuffer.Read(samples, 0, samples.Length);
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.Common
|
||||
{
|
||||
@ -12,7 +14,8 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
private byte[] _buffer;
|
||||
private MemoryOwner<byte> _bufferOwner;
|
||||
private Memory<byte> _buffer;
|
||||
private int _size;
|
||||
private int _headOffset;
|
||||
private int _tailOffset;
|
||||
@ -21,7 +24,8 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
|
||||
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
|
||||
{
|
||||
_buffer = new byte[initialCapacity];
|
||||
_bufferOwner = MemoryOwner<byte>.RentCleared(initialCapacity);
|
||||
_buffer = _bufferOwner.Memory;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
@ -33,6 +37,11 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
|
||||
public void Clear(int size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (size > _size)
|
||||
@ -40,11 +49,6 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
size = _size;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_headOffset = (_headOffset + size) % _buffer.Length;
|
||||
_size -= size;
|
||||
|
||||
@ -58,28 +62,31 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
|
||||
private void SetCapacityLocked(int capacity)
|
||||
{
|
||||
byte[] buffer = new byte[capacity];
|
||||
MemoryOwner<byte> newBufferOwner = MemoryOwner<byte>.RentCleared(capacity);
|
||||
Memory<byte> newBuffer = newBufferOwner.Memory;
|
||||
|
||||
if (_size > 0)
|
||||
{
|
||||
if (_headOffset < _tailOffset)
|
||||
{
|
||||
Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _size);
|
||||
_buffer.Slice(_headOffset, _size).CopyTo(newBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _buffer.Length - _headOffset);
|
||||
Buffer.BlockCopy(_buffer, 0, buffer, _buffer.Length - _headOffset, _tailOffset);
|
||||
_buffer[_headOffset..].CopyTo(newBuffer);
|
||||
_buffer[.._tailOffset].CopyTo(newBuffer[(_buffer.Length - _headOffset)..]);
|
||||
}
|
||||
}
|
||||
|
||||
_buffer = buffer;
|
||||
_bufferOwner.Dispose();
|
||||
|
||||
_bufferOwner = newBufferOwner;
|
||||
_buffer = newBuffer;
|
||||
_headOffset = 0;
|
||||
_tailOffset = _size;
|
||||
}
|
||||
|
||||
|
||||
public void Write<T>(T[] buffer, int index, int count)
|
||||
public void Write(ReadOnlySpan<byte> buffer, int index, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
@ -99,17 +106,17 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
|
||||
if (tailLength >= count)
|
||||
{
|
||||
Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
|
||||
buffer.Slice(index, count).CopyTo(_buffer.Span[_tailOffset..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, tailLength);
|
||||
Buffer.BlockCopy(buffer, index + tailLength, _buffer, 0, count - tailLength);
|
||||
buffer.Slice(index, tailLength).CopyTo(_buffer.Span[_tailOffset..]);
|
||||
buffer.Slice(index + tailLength, count - tailLength).CopyTo(_buffer.Span);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
|
||||
buffer.Slice(index, count).CopyTo(_buffer.Span[_tailOffset..]);
|
||||
}
|
||||
|
||||
_size += count;
|
||||
@ -117,8 +124,13 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
}
|
||||
}
|
||||
|
||||
public int Read<T>(T[] buffer, int index, int count)
|
||||
public int Read(Span<byte> buffer, int index, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (count > _size)
|
||||
@ -126,14 +138,9 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
count = _size;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_headOffset < _tailOffset)
|
||||
{
|
||||
Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
|
||||
_buffer.Span.Slice(_headOffset, count).CopyTo(buffer[index..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -141,12 +148,12 @@ namespace Ryujinx.Audio.Backends.Common
|
||||
|
||||
if (tailLength >= count)
|
||||
{
|
||||
Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
|
||||
_buffer.Span.Slice(_headOffset, count).CopyTo(buffer[index..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(_buffer, _headOffset, buffer, index, tailLength);
|
||||
Buffer.BlockCopy(_buffer, 0, buffer, index + tailLength, count - tailLength);
|
||||
_buffer.Span.Slice(_headOffset, tailLength).CopyTo(buffer[index..]);
|
||||
_buffer.Span[..(count - tailLength)].CopyTo(buffer[(index + tailLength)..]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,12 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
|
||||
public static bool IsSupported => true;
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get => _realDriver.Volume;
|
||||
set => _realDriver.Volume = value;
|
||||
}
|
||||
|
||||
public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice)
|
||||
{
|
||||
_realDriver = realDevice;
|
||||
@ -90,7 +96,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
throw new ArgumentException("No valid sample format configuration found!");
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
{
|
||||
if (channelCount == 0)
|
||||
{
|
||||
@ -102,8 +108,6 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
sampleRate = Constants.TargetSampleRate;
|
||||
}
|
||||
|
||||
volume = Math.Clamp(volume, 0, 1);
|
||||
|
||||
if (!_realDriver.SupportsDirection(direction))
|
||||
{
|
||||
if (direction == Direction.Input)
|
||||
@ -119,7 +123,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
|
||||
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
||||
|
||||
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount, volume);
|
||||
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount);
|
||||
|
||||
if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat)
|
||||
{
|
||||
|
@ -14,13 +14,17 @@ namespace Ryujinx.Audio.Backends.Dummy
|
||||
|
||||
public static bool IsSupported => true;
|
||||
|
||||
public float Volume { get; set; }
|
||||
|
||||
public DummyHardwareDeviceDriver()
|
||||
{
|
||||
_updateRequiredEvent = new ManualResetEvent(false);
|
||||
_pauseEvent = new ManualResetEvent(true);
|
||||
|
||||
Volume = 1f;
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
{
|
||||
if (sampleRate == 0)
|
||||
{
|
||||
@ -34,7 +38,7 @@ namespace Ryujinx.Audio.Backends.Dummy
|
||||
|
||||
if (direction == Direction.Output)
|
||||
{
|
||||
return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||
return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
}
|
||||
|
||||
return new DummyHardwareDeviceSessionInput(this, memoryManager);
|
||||
|
@ -13,9 +13,9 @@ namespace Ryujinx.Audio.Backends.Dummy
|
||||
|
||||
private ulong _playedSampleCount;
|
||||
|
||||
public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_volume = requestedVolume;
|
||||
_volume = 1f;
|
||||
_manager = manager;
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,6 @@ namespace Ryujinx.Audio.Input
|
||||
/// </summary>
|
||||
/// <param name="filtered">If true, filter disconnected devices</param>
|
||||
/// <returns>The list of all audio inputs name</returns>
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public string[] ListAudioIns(bool filtered)
|
||||
{
|
||||
if (filtered)
|
||||
@ -176,7 +175,6 @@ namespace Ryujinx.Audio.Input
|
||||
|
||||
return new[] { Constants.DefaultDeviceInputName };
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
/// <summary>
|
||||
/// Open a new <see cref="AudioInputSystem"/>.
|
||||
@ -188,8 +186,6 @@ namespace Ryujinx.Audio.Input
|
||||
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
||||
/// <param name="sampleFormat">The sample format to use</param>
|
||||
/// <param name="parameter">The user configuration</param>
|
||||
/// <param name="appletResourceUserId">The applet resource user id of the application</param>
|
||||
/// <param name="processHandle">The process handle of the application</param>
|
||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
|
||||
public ResultCode OpenAudioIn(out string outputDeviceName,
|
||||
out AudioOutputConfiguration outputConfiguration,
|
||||
@ -197,9 +193,7 @@ namespace Ryujinx.Audio.Input
|
||||
IVirtualMemoryManager memoryManager,
|
||||
string inputDeviceName,
|
||||
SampleFormat sampleFormat,
|
||||
ref AudioInputConfiguration parameter,
|
||||
ulong appletResourceUserId,
|
||||
uint processHandle)
|
||||
ref AudioInputConfiguration parameter)
|
||||
{
|
||||
int sessionId = AcquireSessionId();
|
||||
|
||||
|
@ -13,9 +13,9 @@ namespace Ryujinx.Audio.Integration
|
||||
|
||||
private readonly byte[] _buffer;
|
||||
|
||||
public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate, float volume)
|
||||
public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate)
|
||||
{
|
||||
_session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount, volume);
|
||||
_session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount);
|
||||
_channelCount = channelCount;
|
||||
_sampleRate = sampleRate;
|
||||
_currentBufferTag = 0;
|
||||
|
@ -16,7 +16,9 @@ namespace Ryujinx.Audio.Integration
|
||||
Output,
|
||||
}
|
||||
|
||||
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume = 1f);
|
||||
float Volume { get; set; }
|
||||
|
||||
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount);
|
||||
|
||||
ManualResetEvent GetUpdateRequiredEvent();
|
||||
ManualResetEvent GetPauseEvent();
|
||||
|
@ -165,12 +165,10 @@ namespace Ryujinx.Audio.Output
|
||||
/// Get the list of all audio outputs name.
|
||||
/// </summary>
|
||||
/// <returns>The list of all audio outputs name</returns>
|
||||
#pragma warning disable CA1822 // Mark member as static
|
||||
public string[] ListAudioOuts()
|
||||
{
|
||||
return new[] { Constants.DefaultDeviceOutputName };
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
|
||||
/// <summary>
|
||||
/// Open a new <see cref="AudioOutputSystem"/>.
|
||||
@ -182,9 +180,6 @@ namespace Ryujinx.Audio.Output
|
||||
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
||||
/// <param name="sampleFormat">The sample format to use</param>
|
||||
/// <param name="parameter">The user configuration</param>
|
||||
/// <param name="appletResourceUserId">The applet resource user id of the application</param>
|
||||
/// <param name="processHandle">The process handle of the application</param>
|
||||
/// <param name="volume">The volume level to request</param>
|
||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
|
||||
public ResultCode OpenAudioOut(out string outputDeviceName,
|
||||
out AudioOutputConfiguration outputConfiguration,
|
||||
@ -192,16 +187,13 @@ namespace Ryujinx.Audio.Output
|
||||
IVirtualMemoryManager memoryManager,
|
||||
string inputDeviceName,
|
||||
SampleFormat sampleFormat,
|
||||
ref AudioInputConfiguration parameter,
|
||||
ulong appletResourceUserId,
|
||||
uint processHandle,
|
||||
float volume)
|
||||
ref AudioInputConfiguration parameter)
|
||||
{
|
||||
int sessionId = AcquireSessionId();
|
||||
|
||||
_sessionsBufferEvents[sessionId].Clear();
|
||||
|
||||
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount, volume);
|
||||
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount);
|
||||
|
||||
AudioOutputSystem audioOut = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
|
||||
|
||||
@ -234,41 +226,6 @@ namespace Ryujinx.Audio.Output
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the volume for all output devices.
|
||||
/// </summary>
|
||||
/// <param name="volume">The volume to set.</param>
|
||||
public void SetVolume(float volume)
|
||||
{
|
||||
if (_sessions != null)
|
||||
{
|
||||
foreach (AudioOutputSystem session in _sessions)
|
||||
{
|
||||
session?.SetVolume(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the volume for all output devices.
|
||||
/// </summary>
|
||||
/// <returns>A float indicating the volume level.</returns>
|
||||
public float GetVolume()
|
||||
{
|
||||
if (_sessions != null)
|
||||
{
|
||||
foreach (AudioOutputSystem session in _sessions)
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
return session.GetVolume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||
public ulong Flags;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an error during <see cref="Server.AudioRenderSystem.Update(System.Memory{byte}, System.Memory{byte}, System.ReadOnlyMemory{byte})"/>.
|
||||
/// Represents an error during <see cref="Server.AudioRenderSystem.Update(System.Memory{byte}, System.Memory{byte}, System.Buffers.ReadOnlySequence{byte})"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ErrorInfo
|
||||
|
@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
|
||||
namespace Ryujinx.Audio.Renderer.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Update data header used for input and output of <see cref="Server.AudioRenderSystem.Update(System.Memory{byte}, System.Memory{byte}, System.ReadOnlyMemory{byte})"/>.
|
||||
/// Update data header used for input and output of <see cref="Server.AudioRenderSystem.Update(System.Memory{byte}, System.Memory{byte}, System.Buffers.ReadOnlySequence{byte})"/>.
|
||||
/// </summary>
|
||||
public struct UpdateDataHeader
|
||||
{
|
||||
|
@ -15,7 +15,6 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||
{
|
||||
public const int Align = 0x10;
|
||||
public const int BiquadStateOffset = 0x0;
|
||||
public const int BiquadStateSize = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// The state of the biquad filters of this voice.
|
||||
|
@ -45,7 +45,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
_event = new ManualResetEvent(false);
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0051 // Remove unused private member
|
||||
private static uint GetHardwareChannelCount(IHardwareDeviceDriver deviceDriver)
|
||||
{
|
||||
// Get the real device driver (In case the compat layer is on top of it).
|
||||
@ -59,9 +58,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
// NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible.
|
||||
return 2;
|
||||
}
|
||||
#pragma warning restore IDE0051
|
||||
|
||||
public void Start(IHardwareDeviceDriver deviceDriver, float volume)
|
||||
public void Start(IHardwareDeviceDriver deviceDriver)
|
||||
{
|
||||
OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
|
||||
|
||||
@ -70,7 +68,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
for (int i = 0; i < OutputDevices.Length; i++)
|
||||
{
|
||||
// TODO: Don't hardcode sample rate.
|
||||
OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate, volume);
|
||||
OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate);
|
||||
}
|
||||
|
||||
_mailbox = new Mailbox<MailboxMessage>();
|
||||
@ -231,33 +229,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
_mailbox.SendResponse(MailboxMessage.Stop);
|
||||
}
|
||||
|
||||
public float GetVolume()
|
||||
{
|
||||
if (OutputDevices != null)
|
||||
{
|
||||
foreach (IHardwareDevice outputDevice in OutputDevices)
|
||||
{
|
||||
if (outputDevice != null)
|
||||
{
|
||||
return outputDevice.GetVolume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public void SetVolume(float volume)
|
||||
{
|
||||
if (OutputDevices != null)
|
||||
{
|
||||
foreach (IHardwareDevice outputDevice in OutputDevices)
|
||||
{
|
||||
outputDevice?.SetVolume(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
@ -269,6 +240,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
if (disposing)
|
||||
{
|
||||
_event.Dispose();
|
||||
_mailbox?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
/// <param name="parameter">The biquad filter parameter</param>
|
||||
/// <param name="state">The biquad filter state</param>
|
||||
/// <param name="outputBuffer">The output buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to read the samples from</param>
|
||||
/// <param name="sampleCount">The count of samples to process</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ProcessBiquadFilter(ref BiquadFilterParameter parameter, ref BiquadFilterState state, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
|
||||
public static void ProcessBiquadFilter(
|
||||
ref BiquadFilterParameter parameter,
|
||||
ref BiquadFilterState state,
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
uint sampleCount)
|
||||
{
|
||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
||||
@ -40,6 +45,96 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a single biquad filter and mix the result into the output buffer.
|
||||
/// </summary>
|
||||
/// <remarks>This is implemented with a direct form 1.</remarks>
|
||||
/// <param name="parameter">The biquad filter parameter</param>
|
||||
/// <param name="state">The biquad filter state</param>
|
||||
/// <param name="outputBuffer">The output buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to read the samples from</param>
|
||||
/// <param name="sampleCount">The count of samples to process</param>
|
||||
/// <param name="volume">Mix volume</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ProcessBiquadFilterAndMix(
|
||||
ref BiquadFilterParameter parameter,
|
||||
ref BiquadFilterState state,
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
uint sampleCount,
|
||||
float volume)
|
||||
{
|
||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
||||
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
|
||||
|
||||
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
|
||||
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
float input = inputBuffer[i];
|
||||
float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
|
||||
|
||||
state.State1 = state.State0;
|
||||
state.State0 = input;
|
||||
state.State3 = state.State2;
|
||||
state.State2 = output;
|
||||
|
||||
outputBuffer[i] += FloatingPointHelper.MultiplyRoundUp(output, volume);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a single biquad filter and mix the result into the output buffer with volume ramp.
|
||||
/// </summary>
|
||||
/// <remarks>This is implemented with a direct form 1.</remarks>
|
||||
/// <param name="parameter">The biquad filter parameter</param>
|
||||
/// <param name="state">The biquad filter state</param>
|
||||
/// <param name="outputBuffer">The output buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to read the samples from</param>
|
||||
/// <param name="sampleCount">The count of samples to process</param>
|
||||
/// <param name="volume">Initial mix volume</param>
|
||||
/// <param name="ramp">Volume increment step</param>
|
||||
/// <returns>Last filtered sample value</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ProcessBiquadFilterAndMixRamp(
|
||||
ref BiquadFilterParameter parameter,
|
||||
ref BiquadFilterState state,
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
uint sampleCount,
|
||||
float volume,
|
||||
float ramp)
|
||||
{
|
||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
||||
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
|
||||
|
||||
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
|
||||
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
|
||||
|
||||
float mixState = 0f;
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
float input = inputBuffer[i];
|
||||
float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
|
||||
|
||||
state.State1 = state.State0;
|
||||
state.State0 = input;
|
||||
state.State3 = state.State2;
|
||||
state.State2 = output;
|
||||
|
||||
mixState = FloatingPointHelper.MultiplyRoundUp(output, volume);
|
||||
|
||||
outputBuffer[i] += mixState;
|
||||
volume += ramp;
|
||||
}
|
||||
|
||||
return mixState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply multiple biquad filter.
|
||||
/// </summary>
|
||||
@ -47,10 +142,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
/// <param name="parameters">The biquad filter parameter</param>
|
||||
/// <param name="states">The biquad filter state</param>
|
||||
/// <param name="outputBuffer">The output buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to read the samples from</param>
|
||||
/// <param name="sampleCount">The count of samples to process</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ProcessBiquadFilter(ReadOnlySpan<BiquadFilterParameter> parameters, Span<BiquadFilterState> states, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
|
||||
public static void ProcessBiquadFilter(
|
||||
ReadOnlySpan<BiquadFilterParameter> parameters,
|
||||
Span<BiquadFilterState> states,
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
uint sampleCount)
|
||||
{
|
||||
for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++)
|
||||
{
|
||||
@ -67,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
float input = inputBuffer[i];
|
||||
float input = stageIndex != 0 ? outputBuffer[i] : inputBuffer[i];
|
||||
float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
|
||||
|
||||
state.State1 = state.State0;
|
||||
@ -79,5 +179,129 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply double biquad filter and mix the result into the output buffer.
|
||||
/// </summary>
|
||||
/// <remarks>This is implemented with a direct form 1.</remarks>
|
||||
/// <param name="parameters">The biquad filter parameter</param>
|
||||
/// <param name="states">The biquad filter state</param>
|
||||
/// <param name="outputBuffer">The output buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to read the samples from</param>
|
||||
/// <param name="sampleCount">The count of samples to process</param>
|
||||
/// <param name="volume">Mix volume</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ProcessDoubleBiquadFilterAndMix(
|
||||
ref BiquadFilterParameter parameter0,
|
||||
ref BiquadFilterParameter parameter1,
|
||||
ref BiquadFilterState state0,
|
||||
ref BiquadFilterState state1,
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
uint sampleCount,
|
||||
float volume)
|
||||
{
|
||||
float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
|
||||
float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
|
||||
|
||||
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
|
||||
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
|
||||
|
||||
float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
|
||||
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
|
||||
|
||||
float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
|
||||
float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
float input = inputBuffer[i];
|
||||
float output = input * a00 + state0.State0 * a10 + state0.State1 * a20 + state0.State2 * b10 + state0.State3 * b20;
|
||||
|
||||
state0.State1 = state0.State0;
|
||||
state0.State0 = input;
|
||||
state0.State3 = state0.State2;
|
||||
state0.State2 = output;
|
||||
|
||||
input = output;
|
||||
output = input * a01 + state1.State0 * a11 + state1.State1 * a21 + state1.State2 * b11 + state1.State3 * b21;
|
||||
|
||||
state1.State1 = state1.State0;
|
||||
state1.State0 = input;
|
||||
state1.State3 = state1.State2;
|
||||
state1.State2 = output;
|
||||
|
||||
outputBuffer[i] += FloatingPointHelper.MultiplyRoundUp(output, volume);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply double biquad filter and mix the result into the output buffer with volume ramp.
|
||||
/// </summary>
|
||||
/// <remarks>This is implemented with a direct form 1.</remarks>
|
||||
/// <param name="parameters">The biquad filter parameter</param>
|
||||
/// <param name="states">The biquad filter state</param>
|
||||
/// <param name="outputBuffer">The output buffer to write the result</param>
|
||||
/// <param name="inputBuffer">The input buffer to read the samples from</param>
|
||||
/// <param name="sampleCount">The count of samples to process</param>
|
||||
/// <param name="volume">Initial mix volume</param>
|
||||
/// <param name="ramp">Volume increment step</param>
|
||||
/// <returns>Last filtered sample value</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ProcessDoubleBiquadFilterAndMixRamp(
|
||||
ref BiquadFilterParameter parameter0,
|
||||
ref BiquadFilterParameter parameter1,
|
||||
ref BiquadFilterState state0,
|
||||
ref BiquadFilterState state1,
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
uint sampleCount,
|
||||
float volume,
|
||||
float ramp)
|
||||
{
|
||||
float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
|
||||
float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
|
||||
|
||||
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
|
||||
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
|
||||
|
||||
float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
|
||||
float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
|
||||
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
|
||||
|
||||
float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
|
||||
float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
|
||||
|
||||
float mixState = 0f;
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
float input = inputBuffer[i];
|
||||
float output = input * a00 + state0.State0 * a10 + state0.State1 * a20 + state0.State2 * b10 + state0.State3 * b20;
|
||||
|
||||
state0.State1 = state0.State0;
|
||||
state0.State0 = input;
|
||||
state0.State3 = state0.State2;
|
||||
state0.State2 = output;
|
||||
|
||||
input = output;
|
||||
output = input * a01 + state1.State0 * a11 + state1.State1 * a21 + state1.State2 * b11 + state1.State3 * b21;
|
||||
|
||||
state1.State1 = state1.State0;
|
||||
state1.State0 = input;
|
||||
state1.State3 = state1.State2;
|
||||
state1.State2 = output;
|
||||
|
||||
mixState = FloatingPointHelper.MultiplyRoundUp(output, volume);
|
||||
|
||||
outputBuffer[i] += mixState;
|
||||
volume += ramp;
|
||||
}
|
||||
|
||||
return mixState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,123 @@
|
||||
using Ryujinx.Audio.Renderer.Common;
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
public class BiquadFilterAndMixCommand : ICommand
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public CommandType CommandType => CommandType.BiquadFilterAndMix;
|
||||
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ushort InputBufferIndex { get; }
|
||||
public ushort OutputBufferIndex { get; }
|
||||
|
||||
private BiquadFilterParameter _parameter;
|
||||
|
||||
public Memory<BiquadFilterState> BiquadFilterState { get; }
|
||||
public Memory<BiquadFilterState> PreviousBiquadFilterState { get; }
|
||||
|
||||
public Memory<VoiceUpdateState> State { get; }
|
||||
|
||||
public int LastSampleIndex { get; }
|
||||
|
||||
public float Volume0 { get; }
|
||||
public float Volume1 { get; }
|
||||
|
||||
public bool NeedInitialization { get; }
|
||||
public bool HasVolumeRamp { get; }
|
||||
public bool IsFirstMixBuffer { get; }
|
||||
|
||||
public BiquadFilterAndMixCommand(
|
||||
float volume0,
|
||||
float volume1,
|
||||
uint inputBufferIndex,
|
||||
uint outputBufferIndex,
|
||||
int lastSampleIndex,
|
||||
Memory<VoiceUpdateState> state,
|
||||
ref BiquadFilterParameter filter,
|
||||
Memory<BiquadFilterState> biquadFilterState,
|
||||
Memory<BiquadFilterState> previousBiquadFilterState,
|
||||
bool needInitialization,
|
||||
bool hasVolumeRamp,
|
||||
bool isFirstMixBuffer,
|
||||
int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
|
||||
InputBufferIndex = (ushort)inputBufferIndex;
|
||||
OutputBufferIndex = (ushort)outputBufferIndex;
|
||||
|
||||
_parameter = filter;
|
||||
BiquadFilterState = biquadFilterState;
|
||||
PreviousBiquadFilterState = previousBiquadFilterState;
|
||||
|
||||
State = state;
|
||||
LastSampleIndex = lastSampleIndex;
|
||||
|
||||
Volume0 = volume0;
|
||||
Volume1 = volume1;
|
||||
|
||||
NeedInitialization = needInitialization;
|
||||
HasVolumeRamp = hasVolumeRamp;
|
||||
IsFirstMixBuffer = isFirstMixBuffer;
|
||||
}
|
||||
|
||||
public void Process(CommandList context)
|
||||
{
|
||||
ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndex);
|
||||
Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex);
|
||||
|
||||
if (NeedInitialization)
|
||||
{
|
||||
// If there is no previous state, initialize to zero.
|
||||
|
||||
BiquadFilterState.Span[0] = new BiquadFilterState();
|
||||
}
|
||||
else if (IsFirstMixBuffer)
|
||||
{
|
||||
// This is the first buffer, set previous state to current state.
|
||||
|
||||
PreviousBiquadFilterState.Span[0] = BiquadFilterState.Span[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rewind the current state by copying back the previous state.
|
||||
|
||||
BiquadFilterState.Span[0] = PreviousBiquadFilterState.Span[0];
|
||||
}
|
||||
|
||||
if (HasVolumeRamp)
|
||||
{
|
||||
float volume = Volume0;
|
||||
float ramp = (Volume1 - Volume0) / (int)context.SampleCount;
|
||||
|
||||
State.Span[0].LastSamples[LastSampleIndex] = BiquadFilterHelper.ProcessBiquadFilterAndMixRamp(
|
||||
ref _parameter,
|
||||
ref BiquadFilterState.Span[0],
|
||||
outputBuffer,
|
||||
inputBuffer,
|
||||
context.SampleCount,
|
||||
volume,
|
||||
ramp);
|
||||
}
|
||||
else
|
||||
{
|
||||
BiquadFilterHelper.ProcessBiquadFilterAndMix(
|
||||
ref _parameter,
|
||||
ref BiquadFilterState.Span[0],
|
||||
outputBuffer,
|
||||
inputBuffer,
|
||||
context.SampleCount,
|
||||
Volume1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -30,8 +30,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
CopyMixBuffer,
|
||||
LimiterVersion1,
|
||||
LimiterVersion2,
|
||||
GroupedBiquadFilter,
|
||||
MultiTapBiquadFilter,
|
||||
CaptureBuffer,
|
||||
Compressor,
|
||||
BiquadFilterAndMix,
|
||||
MultiTapBiquadFilterAndMix,
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public Memory<VoiceUpdateState> State { get; }
|
||||
|
||||
public MixRampGroupedCommand(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> volume0, Span<float> volume1, Memory<VoiceUpdateState> state, int nodeId)
|
||||
public MixRampGroupedCommand(
|
||||
uint mixBufferCount,
|
||||
uint inputBufferIndex,
|
||||
uint outputBufferIndex,
|
||||
ReadOnlySpan<float> volume0,
|
||||
ReadOnlySpan<float> volume1,
|
||||
Memory<VoiceUpdateState> state,
|
||||
int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
MixBufferCount = mixBufferCount;
|
||||
@ -48,7 +55,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float ProcessMixRampGrouped(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, float volume0, float volume1, int sampleCount)
|
||||
private static float ProcessMixRampGrouped(
|
||||
Span<float> outputBuffer,
|
||||
ReadOnlySpan<float> inputBuffer,
|
||||
float volume0,
|
||||
float volume1,
|
||||
int sampleCount)
|
||||
{
|
||||
float ramp = (volume1 - volume0) / sampleCount;
|
||||
float volume = volume0;
|
||||
|
@ -0,0 +1,145 @@
|
||||
using Ryujinx.Audio.Renderer.Common;
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
public class MultiTapBiquadFilterAndMixCommand : ICommand
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public CommandType CommandType => CommandType.MultiTapBiquadFilterAndMix;
|
||||
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ushort InputBufferIndex { get; }
|
||||
public ushort OutputBufferIndex { get; }
|
||||
|
||||
private BiquadFilterParameter _parameter0;
|
||||
private BiquadFilterParameter _parameter1;
|
||||
|
||||
public Memory<BiquadFilterState> BiquadFilterState0 { get; }
|
||||
public Memory<BiquadFilterState> BiquadFilterState1 { get; }
|
||||
public Memory<BiquadFilterState> PreviousBiquadFilterState0 { get; }
|
||||
public Memory<BiquadFilterState> PreviousBiquadFilterState1 { get; }
|
||||
|
||||
public Memory<VoiceUpdateState> State { get; }
|
||||
|
||||
public int LastSampleIndex { get; }
|
||||
|
||||
public float Volume0 { get; }
|
||||
public float Volume1 { get; }
|
||||
|
||||
public bool NeedInitialization0 { get; }
|
||||
public bool NeedInitialization1 { get; }
|
||||
public bool HasVolumeRamp { get; }
|
||||
public bool IsFirstMixBuffer { get; }
|
||||
|
||||
public MultiTapBiquadFilterAndMixCommand(
|
||||
float volume0,
|
||||
float volume1,
|
||||
uint inputBufferIndex,
|
||||
uint outputBufferIndex,
|
||||
int lastSampleIndex,
|
||||
Memory<VoiceUpdateState> state,
|
||||
ref BiquadFilterParameter filter0,
|
||||
ref BiquadFilterParameter filter1,
|
||||
Memory<BiquadFilterState> biquadFilterState0,
|
||||
Memory<BiquadFilterState> biquadFilterState1,
|
||||
Memory<BiquadFilterState> previousBiquadFilterState0,
|
||||
Memory<BiquadFilterState> previousBiquadFilterState1,
|
||||
bool needInitialization0,
|
||||
bool needInitialization1,
|
||||
bool hasVolumeRamp,
|
||||
bool isFirstMixBuffer,
|
||||
int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
|
||||
InputBufferIndex = (ushort)inputBufferIndex;
|
||||
OutputBufferIndex = (ushort)outputBufferIndex;
|
||||
|
||||
_parameter0 = filter0;
|
||||
_parameter1 = filter1;
|
||||
BiquadFilterState0 = biquadFilterState0;
|
||||
BiquadFilterState1 = biquadFilterState1;
|
||||
PreviousBiquadFilterState0 = previousBiquadFilterState0;
|
||||
PreviousBiquadFilterState1 = previousBiquadFilterState1;
|
||||
|
||||
State = state;
|
||||
LastSampleIndex = lastSampleIndex;
|
||||
|
||||
Volume0 = volume0;
|
||||
Volume1 = volume1;
|
||||
|
||||
NeedInitialization0 = needInitialization0;
|
||||
NeedInitialization1 = needInitialization1;
|
||||
HasVolumeRamp = hasVolumeRamp;
|
||||
IsFirstMixBuffer = isFirstMixBuffer;
|
||||
}
|
||||
|
||||
private void UpdateState(Memory<BiquadFilterState> state, Memory<BiquadFilterState> previousState, bool needInitialization)
|
||||
{
|
||||
if (needInitialization)
|
||||
{
|
||||
// If there is no previous state, initialize to zero.
|
||||
|
||||
state.Span[0] = new BiquadFilterState();
|
||||
}
|
||||
else if (IsFirstMixBuffer)
|
||||
{
|
||||
// This is the first buffer, set previous state to current state.
|
||||
|
||||
previousState.Span[0] = state.Span[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rewind the current state by copying back the previous state.
|
||||
|
||||
state.Span[0] = previousState.Span[0];
|
||||
}
|
||||
}
|
||||
|
||||
public void Process(CommandList context)
|
||||
{
|
||||
ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndex);
|
||||
Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex);
|
||||
|
||||
UpdateState(BiquadFilterState0, PreviousBiquadFilterState0, NeedInitialization0);
|
||||
UpdateState(BiquadFilterState1, PreviousBiquadFilterState1, NeedInitialization1);
|
||||
|
||||
if (HasVolumeRamp)
|
||||
{
|
||||
float volume = Volume0;
|
||||
float ramp = (Volume1 - Volume0) / (int)context.SampleCount;
|
||||
|
||||
State.Span[0].LastSamples[LastSampleIndex] = BiquadFilterHelper.ProcessDoubleBiquadFilterAndMixRamp(
|
||||
ref _parameter0,
|
||||
ref _parameter1,
|
||||
ref BiquadFilterState0.Span[0],
|
||||
ref BiquadFilterState1.Span[0],
|
||||
outputBuffer,
|
||||
inputBuffer,
|
||||
context.SampleCount,
|
||||
volume,
|
||||
ramp);
|
||||
}
|
||||
else
|
||||
{
|
||||
BiquadFilterHelper.ProcessDoubleBiquadFilterAndMix(
|
||||
ref _parameter0,
|
||||
ref _parameter1,
|
||||
ref BiquadFilterState0.Span[0],
|
||||
ref BiquadFilterState1.Span[0],
|
||||
outputBuffer,
|
||||
inputBuffer,
|
||||
context.SampleCount,
|
||||
Volume1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,13 +4,13 @@ using System;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
public class GroupedBiquadFilterCommand : ICommand
|
||||
public class MultiTapBiquadFilterCommand : ICommand
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public CommandType CommandType => CommandType.GroupedBiquadFilter;
|
||||
public CommandType CommandType => CommandType.MultiTapBiquadFilter;
|
||||
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
private readonly int _outputBufferIndex;
|
||||
private readonly bool[] _isInitialized;
|
||||
|
||||
public GroupedBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
||||
public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
||||
{
|
||||
_parameters = filters.ToArray();
|
||||
_biquadFilterStates = biquadFilterStateMemory;
|
@ -2,12 +2,16 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x20)]
|
||||
public struct BiquadFilterState
|
||||
{
|
||||
public float State0;
|
||||
public float State1;
|
||||
public float State2;
|
||||
public float State3;
|
||||
public float State4;
|
||||
public float State5;
|
||||
public float State6;
|
||||
public float State7;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
/// <summary>
|
||||
/// Output information for behaviour.
|
||||
/// </summary>
|
||||
/// <remarks>This is used to report errors to the user during <see cref="Server.AudioRenderSystem.Update(Memory{byte}, Memory{byte}, ReadOnlyMemory{byte})"/> processing.</remarks>
|
||||
/// <remarks>This is used to report errors to the user during <see cref="Server.AudioRenderSystem.Update(Memory{byte}, Memory{byte}, System.Buffers.ReadOnlySequence{byte})"/> processing.</remarks>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct BehaviourErrorInfoOutStatus
|
||||
{
|
||||
|
@ -0,0 +1,43 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic interface for the splitter destination parameters.
|
||||
/// </summary>
|
||||
public interface ISplitterDestinationInParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Target splitter destination data id.
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The mix to output the result of the splitter.
|
||||
/// </summary>
|
||||
int DestinationId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Biquad filter parameters.
|
||||
/// </summary>
|
||||
Array2<BiquadFilterParameter> BiquadFilters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if in use.
|
||||
/// </summary>
|
||||
bool IsUsed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Mix buffer volumes.
|
||||
/// </summary>
|
||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||
Span<float> MixBufferVolume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Check if the magic is valid.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the magic is valid.</returns>
|
||||
bool IsMagicValid();
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -5,10 +6,10 @@ using System.Runtime.InteropServices;
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Input header for a splitter destination update.
|
||||
/// Input header for a splitter destination version 1 update.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct SplitterDestinationInParameter
|
||||
public struct SplitterDestinationInParameterVersion1 : ISplitterDestinationInParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Magic of the input header.
|
||||
@ -41,7 +42,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
/// </summary>
|
||||
private unsafe fixed byte _reserved[3];
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)]
|
||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||
private struct MixArray { }
|
||||
|
||||
/// <summary>
|
||||
@ -50,6 +51,14 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
/// <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<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
|
||||
|
||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// The expected constant of any input header.
|
||||
/// </summary>
|
@ -0,0 +1,81 @@
|
||||
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 SplitterDestinationInParameterVersion2 : 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<BiquadFilterParameter> BiquadFilters;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if in use.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// </summary>
|
||||
private unsafe fixed byte _reserved[11];
|
||||
|
||||
[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<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
|
||||
|
||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Audio.Renderer.Common;
|
||||
using Ryujinx.Audio.Renderer.Dsp.Command;
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
||||
@ -173,6 +174,22 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return ResultCode.WorkBufferTooSmall;
|
||||
}
|
||||
|
||||
Memory<BiquadFilterState> splitterBqfStates = Memory<BiquadFilterState>.Empty;
|
||||
|
||||
if (_behaviourContext.IsBiquadFilterParameterForSplitterEnabled() &&
|
||||
parameter.SplitterCount > 0 &&
|
||||
parameter.SplitterDestinationCount > 0)
|
||||
{
|
||||
splitterBqfStates = workBufferAllocator.Allocate<BiquadFilterState>(parameter.SplitterDestinationCount * SplitterContext.BqfStatesPerDestination, 0x10);
|
||||
|
||||
if (splitterBqfStates.IsEmpty)
|
||||
{
|
||||
return ResultCode.WorkBufferTooSmall;
|
||||
}
|
||||
|
||||
splitterBqfStates.Span.Clear();
|
||||
}
|
||||
|
||||
// Invalidate DSP cache on what was currently allocated with workBuffer.
|
||||
AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
|
||||
|
||||
@ -292,7 +309,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu);
|
||||
}
|
||||
|
||||
if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator))
|
||||
if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator, splitterBqfStates))
|
||||
{
|
||||
return ResultCode.WorkBufferTooSmall;
|
||||
}
|
||||
@ -386,7 +403,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
}
|
||||
}
|
||||
|
||||
public ResultCode Update(Memory<byte> output, Memory<byte> performanceOutput, ReadOnlyMemory<byte> input)
|
||||
public ResultCode Update(Memory<byte> output, Memory<byte> performanceOutput, ReadOnlySequence<byte> input)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@ -419,14 +436,16 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return result;
|
||||
}
|
||||
|
||||
result = stateUpdater.UpdateVoices(_voiceContext, _memoryPools);
|
||||
PoolMapper poolMapper = new PoolMapper(_processHandle, _memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
|
||||
|
||||
result = stateUpdater.UpdateVoices(_voiceContext, poolMapper);
|
||||
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = stateUpdater.UpdateEffects(_effectContext, _isActive, _memoryPools);
|
||||
result = stateUpdater.UpdateEffects(_effectContext, _isActive, poolMapper);
|
||||
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
@ -450,7 +469,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return result;
|
||||
}
|
||||
|
||||
result = stateUpdater.UpdateSinks(_sinkContext, _memoryPools);
|
||||
result = stateUpdater.UpdateSinks(_sinkContext, poolMapper);
|
||||
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
@ -773,6 +792,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
// Splitter
|
||||
size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter);
|
||||
|
||||
if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled() &&
|
||||
parameter.SplitterCount > 0 &&
|
||||
parameter.SplitterDestinationCount > 0)
|
||||
{
|
||||
size = WorkBufferAllocator.GetTargetSize<BiquadFilterState>(size, parameter.SplitterDestinationCount * SplitterContext.BqfStatesPerDestination, 0x10);
|
||||
}
|
||||
|
||||
// DSP Voice
|
||||
size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align);
|
||||
|
||||
|
@ -177,12 +177,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <summary>
|
||||
/// Start the <see cref="AudioProcessor"/> and worker thread.
|
||||
/// </summary>
|
||||
private void StartLocked(float volume)
|
||||
private void StartLocked()
|
||||
{
|
||||
_isRunning = true;
|
||||
|
||||
// TODO: virtual device mapping (IAudioDevice)
|
||||
Processor.Start(_deviceDriver, volume);
|
||||
Processor.Start(_deviceDriver);
|
||||
|
||||
_workerThread = new Thread(SendCommands)
|
||||
{
|
||||
@ -254,7 +254,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// Register a new <see cref="AudioRenderSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
|
||||
private void Register(AudioRenderSystem renderer, float volume)
|
||||
private void Register(AudioRenderSystem renderer)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
@ -265,7 +265,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
StartLocked(volume);
|
||||
StartLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,8 +312,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
ulong appletResourceUserId,
|
||||
ulong workBufferAddress,
|
||||
ulong workBufferSize,
|
||||
uint processHandle,
|
||||
float volume)
|
||||
uint processHandle)
|
||||
{
|
||||
int sessionId = AcquireSessionId();
|
||||
|
||||
@ -338,7 +337,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
renderer = audioRenderer;
|
||||
|
||||
Register(renderer, volume);
|
||||
Register(renderer);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -350,21 +349,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return result;
|
||||
}
|
||||
|
||||
public float GetVolume()
|
||||
{
|
||||
if (Processor != null)
|
||||
{
|
||||
return Processor.GetVolume();
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public void SetVolume(float volume)
|
||||
{
|
||||
Processor?.SetVolume(volume);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
|
||||
|
||||
@ -44,7 +45,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <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.
|
||||
/// Additionally, the rendering limit percent was incremented to 80%.
|
||||
///
|
||||
/// </summary>
|
||||
/// <remarks>This was added in system update 6.0.0</remarks>
|
||||
public const int Revision5 = 5 << 24;
|
||||
@ -100,10 +100,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <remarks>This was added in system update 14.0.0 but some changes were made in 15.0.0</remarks>
|
||||
public const int Revision11 = 11 << 24;
|
||||
|
||||
/// <summary>
|
||||
/// REV12:
|
||||
/// Two new commands were added to for biquad filtering and mixing (with optinal volume ramp) on the same command.
|
||||
/// Splitter destinations can now specify up to two biquad filtering parameters, used for filtering the buffer before mixing.
|
||||
/// </summary>
|
||||
/// <remarks>This was added in system update 17.0.0</remarks>
|
||||
public const int Revision12 = 12 << 24;
|
||||
|
||||
/// <summary>
|
||||
/// Last revision supported by the implementation.
|
||||
/// </summary>
|
||||
public const int LastRevision = Revision11;
|
||||
public const int LastRevision = Revision12;
|
||||
|
||||
/// <summary>
|
||||
/// Target revision magic supported by the implementation.
|
||||
@ -211,7 +219,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <summary>
|
||||
/// Check if the audio renderer should fix the GC-ADPCM context not being provided to the DSP.
|
||||
/// </summary>
|
||||
/// <returns>True if if the audio renderer should fix it.</returns>
|
||||
/// <returns>True if the audio renderer should fix it.</returns>
|
||||
public bool IsAdpcmLoopContextBugFixed()
|
||||
{
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision2);
|
||||
@ -273,7 +281,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the audio renderer should trust the user destination count in <see cref="Splitter.SplitterState.Update(Splitter.SplitterContext, ref Parameter.SplitterInParameter, ReadOnlySpan{byte})"/>.
|
||||
/// Check if the audio renderer should trust the user destination count in <see cref="Renderer.Server.Splitter.SplitterState.Update(Renderer.Server.Splitter.SplitterContext, Renderer.Parameter.SplitterInParameter, SequenceReader{byte})"/>.
|
||||
/// </summary>
|
||||
/// <returns>True if the audio renderer should trust the user destination count.</returns>
|
||||
public bool IsSplitterBugFixed()
|
||||
@ -353,7 +361,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// Check if the audio renderer should use an optimized Biquad Filter (Direct Form 1) in case of two biquad filters are defined on a voice.
|
||||
/// </summary>
|
||||
/// <returns>True if the audio renderer should use the optimization.</returns>
|
||||
public bool IsBiquadFilterGroupedOptimizationSupported()
|
||||
public bool UseMultiTapBiquadFilterProcessing()
|
||||
{
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
|
||||
}
|
||||
@ -367,6 +375,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision11);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the audio renderer should support biquad filter on splitter.
|
||||
/// </summary>
|
||||
/// <returns>True if the audio renderer support biquad filter on splitter</returns>
|
||||
public bool IsBiquadFilterParameterForSplitterEnabled()
|
||||
{
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||
/// </summary>
|
||||
|
@ -204,7 +204,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="GroupedBiquadFilterCommand"/>.
|
||||
/// Create a new <see cref="MultiTapBiquadFilterCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="baseIndex">The base index of the input and output buffer.</param>
|
||||
/// <param name="filters">The biquad filter parameters.</param>
|
||||
@ -213,9 +213,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <param name="outputBufferOffset">The output buffer offset.</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>
|
||||
public void GenerateGroupedBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
||||
public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
||||
{
|
||||
GroupedBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId);
|
||||
MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId);
|
||||
|
||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||
|
||||
@ -232,7 +232,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <param name="volume">The new volume.</param>
|
||||
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
|
||||
/// <param name="nodeId">The node id associated to this command.</param>
|
||||
public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> previousVolume, Span<float> volume, Memory<VoiceUpdateState> state, int nodeId)
|
||||
public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan<float> previousVolume, ReadOnlySpan<float> volume, Memory<VoiceUpdateState> state, int nodeId)
|
||||
{
|
||||
MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId);
|
||||
|
||||
@ -260,6 +260,120 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
AddCommand(command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="BiquadFilterAndMixCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="previousVolume">The previous volume.</param>
|
||||
/// <param name="volume">The new volume.</param>
|
||||
/// <param name="inputBufferIndex">The input 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="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
|
||||
/// <param name="filter">The biquad filter parameter.</param>
|
||||
/// <param name="biquadFilterState">The biquad state.</param>
|
||||
/// <param name="previousBiquadFilterState">The previous biquad state.</param>
|
||||
/// <param name="needInitialization">Set to true if the biquad filter state needs to be initialized.</param>
|
||||
/// <param name="hasVolumeRamp">Set to true if the mix has volume ramp, and <paramref name="previousVolume"/> should be taken into account.</param>
|
||||
/// <param name="isFirstMixBuffer">Set to true if the buffer is the first mix buffer.</param>
|
||||
/// <param name="nodeId">The node id associated to this command.</param>
|
||||
public void GenerateBiquadFilterAndMix(
|
||||
float previousVolume,
|
||||
float volume,
|
||||
uint inputBufferIndex,
|
||||
uint outputBufferIndex,
|
||||
int lastSampleIndex,
|
||||
Memory<VoiceUpdateState> state,
|
||||
ref BiquadFilterParameter filter,
|
||||
Memory<BiquadFilterState> biquadFilterState,
|
||||
Memory<BiquadFilterState> previousBiquadFilterState,
|
||||
bool needInitialization,
|
||||
bool hasVolumeRamp,
|
||||
bool isFirstMixBuffer,
|
||||
int nodeId)
|
||||
{
|
||||
BiquadFilterAndMixCommand command = new(
|
||||
previousVolume,
|
||||
volume,
|
||||
inputBufferIndex,
|
||||
outputBufferIndex,
|
||||
lastSampleIndex,
|
||||
state,
|
||||
ref filter,
|
||||
biquadFilterState,
|
||||
previousBiquadFilterState,
|
||||
needInitialization,
|
||||
hasVolumeRamp,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||
|
||||
AddCommand(command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="MultiTapBiquadFilterAndMixCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="previousVolume">The previous volume.</param>
|
||||
/// <param name="volume">The new volume.</param>
|
||||
/// <param name="inputBufferIndex">The input 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="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
|
||||
/// <param name="filter0">First biquad filter parameter.</param>
|
||||
/// <param name="filter1">Second biquad filter parameter.</param>
|
||||
/// <param name="biquadFilterState0">First biquad state.</param>
|
||||
/// <param name="biquadFilterState1">Second biquad state.</param>
|
||||
/// <param name="previousBiquadFilterState0">First previous biquad state.</param>
|
||||
/// <param name="previousBiquadFilterState1">Second previous biquad state.</param>
|
||||
/// <param name="needInitialization0">Set to true if the first biquad filter state needs to be initialized.</param>
|
||||
/// <param name="needInitialization1">Set to true if the second biquad filter state needs to be initialized.</param>
|
||||
/// <param name="hasVolumeRamp">Set to true if the mix has volume ramp, and <paramref name="previousVolume"/> should be taken into account.</param>
|
||||
/// <param name="isFirstMixBuffer">Set to true if the buffer is the first mix buffer.</param>
|
||||
/// <param name="nodeId">The node id associated to this command.</param>
|
||||
public void GenerateMultiTapBiquadFilterAndMix(
|
||||
float previousVolume,
|
||||
float volume,
|
||||
uint inputBufferIndex,
|
||||
uint outputBufferIndex,
|
||||
int lastSampleIndex,
|
||||
Memory<VoiceUpdateState> state,
|
||||
ref BiquadFilterParameter filter0,
|
||||
ref BiquadFilterParameter filter1,
|
||||
Memory<BiquadFilterState> biquadFilterState0,
|
||||
Memory<BiquadFilterState> biquadFilterState1,
|
||||
Memory<BiquadFilterState> previousBiquadFilterState0,
|
||||
Memory<BiquadFilterState> previousBiquadFilterState1,
|
||||
bool needInitialization0,
|
||||
bool needInitialization1,
|
||||
bool hasVolumeRamp,
|
||||
bool isFirstMixBuffer,
|
||||
int nodeId)
|
||||
{
|
||||
MultiTapBiquadFilterAndMixCommand command = new(
|
||||
previousVolume,
|
||||
volume,
|
||||
inputBufferIndex,
|
||||
outputBufferIndex,
|
||||
lastSampleIndex,
|
||||
state,
|
||||
ref filter0,
|
||||
ref filter1,
|
||||
biquadFilterState0,
|
||||
biquadFilterState1,
|
||||
previousBiquadFilterState0,
|
||||
previousBiquadFilterState1,
|
||||
needInitialization0,
|
||||
needInitialization1,
|
||||
hasVolumeRamp,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||
|
||||
AddCommand(command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="DepopForMixBuffersCommand"/>.
|
||||
/// </summary>
|
||||
@ -268,7 +382,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <param name="bufferCount">The buffer count.</param>
|
||||
/// <param name="nodeId">The node id associated to this command.</param>
|
||||
/// <param name="sampleRate">The target sample rate in use.</param>
|
||||
public void GenerateDepopForMixBuffersCommand(Memory<float> depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate)
|
||||
public void GenerateDepopForMixBuffers(Memory<float> depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate)
|
||||
{
|
||||
DepopForMixBuffersCommand command = new(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate);
|
||||
|
||||
|
@ -12,6 +12,7 @@ using Ryujinx.Audio.Renderer.Server.Voice;
|
||||
using Ryujinx.Audio.Renderer.Utils;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
@ -46,12 +47,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
|
||||
|
||||
_commandBuffer.GenerateDepopPrepare(dspState,
|
||||
_rendererContext.DepopBuffer,
|
||||
mix.BufferCount,
|
||||
mix.BufferOffset,
|
||||
voiceState.NodeId,
|
||||
voiceState.WasPlaying);
|
||||
_commandBuffer.GenerateDepopPrepare(
|
||||
dspState,
|
||||
_rendererContext.DepopBuffer,
|
||||
mix.BufferCount,
|
||||
mix.BufferOffset,
|
||||
voiceState.NodeId,
|
||||
voiceState.WasPlaying);
|
||||
}
|
||||
else if (voiceState.SplitterId != Constants.UnusedSplitterId)
|
||||
{
|
||||
@ -59,15 +61,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
while (true)
|
||||
{
|
||||
Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++);
|
||||
SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++);
|
||||
|
||||
if (destinationSpan.IsEmpty)
|
||||
if (destination.IsNull)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ref SplitterDestination destination = ref destinationSpan[0];
|
||||
|
||||
if (destination.IsConfigured())
|
||||
{
|
||||
int mixId = destination.DestinationId;
|
||||
@ -76,12 +76,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
ref MixState mix = ref _mixContext.GetState(mixId);
|
||||
|
||||
_commandBuffer.GenerateDepopPrepare(dspState,
|
||||
_rendererContext.DepopBuffer,
|
||||
mix.BufferCount,
|
||||
mix.BufferOffset,
|
||||
voiceState.NodeId,
|
||||
voiceState.WasPlaying);
|
||||
_commandBuffer.GenerateDepopPrepare(
|
||||
dspState,
|
||||
_rendererContext.DepopBuffer,
|
||||
mix.BufferCount,
|
||||
mix.BufferOffset,
|
||||
voiceState.NodeId,
|
||||
voiceState.WasPlaying);
|
||||
|
||||
destination.MarkAsNeedToUpdateInternalState();
|
||||
}
|
||||
@ -95,35 +96,39 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported())
|
||||
{
|
||||
_commandBuffer.GenerateDataSourceVersion2(ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
(ushort)channelIndex,
|
||||
voiceState.NodeId);
|
||||
_commandBuffer.GenerateDataSourceVersion2(
|
||||
ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
(ushort)channelIndex,
|
||||
voiceState.NodeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voiceState.SampleFormat)
|
||||
{
|
||||
case SampleFormat.PcmInt16:
|
||||
_commandBuffer.GeneratePcmInt16DataSourceVersion1(ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
(ushort)channelIndex,
|
||||
voiceState.NodeId);
|
||||
_commandBuffer.GeneratePcmInt16DataSourceVersion1(
|
||||
ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
(ushort)channelIndex,
|
||||
voiceState.NodeId);
|
||||
break;
|
||||
case SampleFormat.PcmFloat:
|
||||
_commandBuffer.GeneratePcmFloatDataSourceVersion1(ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
(ushort)channelIndex,
|
||||
voiceState.NodeId);
|
||||
_commandBuffer.GeneratePcmFloatDataSourceVersion1(
|
||||
ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
(ushort)channelIndex,
|
||||
voiceState.NodeId);
|
||||
break;
|
||||
case SampleFormat.Adpcm:
|
||||
_commandBuffer.GenerateAdpcmDataSourceVersion1(ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
voiceState.NodeId);
|
||||
_commandBuffer.GenerateAdpcmDataSourceVersion1(
|
||||
ref voiceState,
|
||||
dspState,
|
||||
(ushort)_rendererContext.MixBufferCount,
|
||||
voiceState.NodeId);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}");
|
||||
@ -134,14 +139,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
|
||||
{
|
||||
bool supportsOptimizedPath = _rendererContext.BehaviourContext.IsBiquadFilterGroupedOptimizationSupported();
|
||||
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
|
||||
|
||||
if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
|
||||
{
|
||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)];
|
||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
|
||||
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
||||
|
||||
_commandBuffer.GenerateGroupedBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
|
||||
_commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -151,33 +156,134 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
if (filter.Enable)
|
||||
{
|
||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)];
|
||||
|
||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
|
||||
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
||||
|
||||
_commandBuffer.GenerateBiquadFilter(baseIndex,
|
||||
ref filter,
|
||||
stateMemory.Slice(i, 1),
|
||||
bufferOffset,
|
||||
bufferOffset,
|
||||
!voiceState.BiquadFilterNeedInitialization[i],
|
||||
nodeId);
|
||||
_commandBuffer.GenerateBiquadFilter(
|
||||
baseIndex,
|
||||
ref filter,
|
||||
stateMemory.Slice(i, 1),
|
||||
bufferOffset,
|
||||
bufferOffset,
|
||||
!voiceState.BiquadFilterNeedInitialization[i],
|
||||
nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateVoiceMix(Span<float> mixVolumes, Span<float> previousMixVolumes, Memory<VoiceUpdateState> state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId)
|
||||
private void GenerateVoiceMixWithSplitter(
|
||||
SplitterDestination destination,
|
||||
Memory<VoiceUpdateState> state,
|
||||
uint bufferOffset,
|
||||
uint bufferCount,
|
||||
uint bufferIndex,
|
||||
int nodeId)
|
||||
{
|
||||
ReadOnlySpan<float> mixVolumes = destination.MixBufferVolume;
|
||||
ReadOnlySpan<float> previousMixVolumes = destination.PreviousMixBufferVolume;
|
||||
|
||||
ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0);
|
||||
ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1);
|
||||
|
||||
Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
|
||||
|
||||
bool isFirstMixBuffer = true;
|
||||
|
||||
for (int i = 0; i < bufferCount; i++)
|
||||
{
|
||||
float previousMixVolume = previousMixVolumes[i];
|
||||
float mixVolume = mixVolumes[i];
|
||||
|
||||
if (mixVolume != 0.0f || previousMixVolume != 0.0f)
|
||||
{
|
||||
if (bqf0.Enable && bqf1.Enable)
|
||||
{
|
||||
_commandBuffer.GenerateMultiTapBiquadFilterAndMix(
|
||||
previousMixVolume,
|
||||
mixVolume,
|
||||
bufferIndex,
|
||||
bufferOffset + (uint)i,
|
||||
i,
|
||||
state,
|
||||
ref bqf0,
|
||||
ref bqf1,
|
||||
bqfState[..1],
|
||||
bqfState.Slice(1, 1),
|
||||
bqfState.Slice(2, 1),
|
||||
bqfState.Slice(3, 1),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
true,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
destination.UpdateBiquadFilterEnabledPrev(0);
|
||||
destination.UpdateBiquadFilterEnabledPrev(1);
|
||||
}
|
||||
else if (bqf0.Enable)
|
||||
{
|
||||
_commandBuffer.GenerateBiquadFilterAndMix(
|
||||
previousMixVolume,
|
||||
mixVolume,
|
||||
bufferIndex,
|
||||
bufferOffset + (uint)i,
|
||||
i,
|
||||
state,
|
||||
ref bqf0,
|
||||
bqfState[..1],
|
||||
bqfState.Slice(1, 1),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
true,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
destination.UpdateBiquadFilterEnabledPrev(0);
|
||||
}
|
||||
else if (bqf1.Enable)
|
||||
{
|
||||
_commandBuffer.GenerateBiquadFilterAndMix(
|
||||
previousMixVolume,
|
||||
mixVolume,
|
||||
bufferIndex,
|
||||
bufferOffset + (uint)i,
|
||||
i,
|
||||
state,
|
||||
ref bqf1,
|
||||
bqfState[..1],
|
||||
bqfState.Slice(1, 1),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
true,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
destination.UpdateBiquadFilterEnabledPrev(1);
|
||||
}
|
||||
|
||||
isFirstMixBuffer = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateVoiceMix(
|
||||
ReadOnlySpan<float> mixVolumes,
|
||||
ReadOnlySpan<float> previousMixVolumes,
|
||||
Memory<VoiceUpdateState> state,
|
||||
uint bufferOffset,
|
||||
uint bufferCount,
|
||||
uint bufferIndex,
|
||||
int nodeId)
|
||||
{
|
||||
if (bufferCount > Constants.VoiceChannelCountMax)
|
||||
{
|
||||
_commandBuffer.GenerateMixRampGrouped(bufferCount,
|
||||
bufferIndex,
|
||||
bufferOffset,
|
||||
previousMixVolumes,
|
||||
mixVolumes,
|
||||
state,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateMixRampGrouped(
|
||||
bufferCount,
|
||||
bufferIndex,
|
||||
bufferOffset,
|
||||
previousMixVolumes,
|
||||
mixVolumes,
|
||||
state,
|
||||
nodeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -188,13 +294,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
if (mixVolume != 0.0f || previousMixVolume != 0.0f)
|
||||
{
|
||||
_commandBuffer.GenerateMixRamp(previousMixVolume,
|
||||
mixVolume,
|
||||
bufferIndex,
|
||||
bufferOffset + (uint)i,
|
||||
i,
|
||||
state,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateMixRamp(
|
||||
previousMixVolume,
|
||||
mixVolume,
|
||||
bufferIndex,
|
||||
bufferOffset + (uint)i,
|
||||
i,
|
||||
state,
|
||||
nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -271,10 +378,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
||||
}
|
||||
|
||||
_commandBuffer.GenerateVolumeRamp(voiceState.PreviousVolume,
|
||||
voiceState.Volume,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateVolumeRamp(
|
||||
voiceState.PreviousVolume,
|
||||
voiceState.Volume,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
|
||||
if (performanceInitialized)
|
||||
{
|
||||
@ -291,15 +399,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
while (true)
|
||||
{
|
||||
Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
|
||||
SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
|
||||
|
||||
if (destinationSpan.IsEmpty)
|
||||
if (destination.IsNull)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ref SplitterDestination destination = ref destinationSpan[0];
|
||||
|
||||
destinationId += (int)channelsCount;
|
||||
|
||||
if (destination.IsConfigured())
|
||||
@ -310,13 +416,27 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
ref MixState mix = ref _mixContext.GetState(mixId);
|
||||
|
||||
GenerateVoiceMix(destination.MixBufferVolume,
|
||||
destination.PreviousMixBufferVolume,
|
||||
dspStateMemory,
|
||||
mix.BufferOffset,
|
||||
mix.BufferCount,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
if (destination.IsBiquadFilterEnabled())
|
||||
{
|
||||
GenerateVoiceMixWithSplitter(
|
||||
destination,
|
||||
dspStateMemory,
|
||||
mix.BufferOffset,
|
||||
mix.BufferCount,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
GenerateVoiceMix(
|
||||
destination.MixBufferVolume,
|
||||
destination.PreviousMixBufferVolume,
|
||||
dspStateMemory,
|
||||
mix.BufferOffset,
|
||||
mix.BufferCount,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
}
|
||||
|
||||
destination.MarkAsNeedToUpdateInternalState();
|
||||
}
|
||||
@ -337,13 +457,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
||||
}
|
||||
|
||||
GenerateVoiceMix(channelResource.Mix.AsSpan(),
|
||||
channelResource.PreviousMix.AsSpan(),
|
||||
dspStateMemory,
|
||||
mix.BufferOffset,
|
||||
mix.BufferCount,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
GenerateVoiceMix(
|
||||
channelResource.Mix.AsSpan(),
|
||||
channelResource.PreviousMix.AsSpan(),
|
||||
dspStateMemory,
|
||||
mix.BufferOffset,
|
||||
mix.BufferCount,
|
||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||
nodeId);
|
||||
|
||||
if (performanceInitialized)
|
||||
{
|
||||
@ -409,10 +530,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
if (effect.Parameter.Volumes[i] != 0.0f)
|
||||
{
|
||||
_commandBuffer.GenerateMix((uint)bufferOffset + effect.Parameter.Input[i],
|
||||
(uint)bufferOffset + effect.Parameter.Output[i],
|
||||
nodeId,
|
||||
effect.Parameter.Volumes[i]);
|
||||
_commandBuffer.GenerateMix(
|
||||
(uint)bufferOffset + effect.Parameter.Input[i],
|
||||
(uint)bufferOffset + effect.Parameter.Output[i],
|
||||
nodeId,
|
||||
effect.Parameter.Volumes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -447,17 +569,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
updateCount = newUpdateCount;
|
||||
}
|
||||
|
||||
_commandBuffer.GenerateAuxEffect(bufferOffset,
|
||||
effect.Parameter.Input[i],
|
||||
effect.Parameter.Output[i],
|
||||
ref effect.State,
|
||||
effect.IsEnabled,
|
||||
effect.Parameter.BufferStorageSize,
|
||||
effect.State.SendBufferInfoBase,
|
||||
effect.State.ReturnBufferInfoBase,
|
||||
updateCount,
|
||||
writeOffset,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateAuxEffect(
|
||||
bufferOffset,
|
||||
effect.Parameter.Input[i],
|
||||
effect.Parameter.Output[i],
|
||||
ref effect.State,
|
||||
effect.IsEnabled,
|
||||
effect.Parameter.BufferStorageSize,
|
||||
effect.State.SendBufferInfoBase,
|
||||
effect.State.ReturnBufferInfoBase,
|
||||
updateCount,
|
||||
writeOffset,
|
||||
nodeId);
|
||||
|
||||
writeOffset = newUpdateCount;
|
||||
|
||||
@ -500,7 +623,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
if (effect.IsEnabled)
|
||||
{
|
||||
bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
|
||||
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
|
||||
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
|
||||
|
||||
BiquadFilterParameter parameter = new()
|
||||
{
|
||||
@ -512,11 +635,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
|
||||
{
|
||||
_commandBuffer.GenerateBiquadFilter((int)bufferOffset, ref parameter, effect.State.Slice(i, 1),
|
||||
effect.Parameter.Input[i],
|
||||
effect.Parameter.Output[i],
|
||||
needInitialization,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateBiquadFilter(
|
||||
(int)bufferOffset,
|
||||
ref parameter,
|
||||
effect.State.Slice(i, 1),
|
||||
effect.Parameter.Input[i],
|
||||
effect.Parameter.Output[i],
|
||||
needInitialization,
|
||||
nodeId);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -591,15 +717,16 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
updateCount = newUpdateCount;
|
||||
}
|
||||
|
||||
_commandBuffer.GenerateCaptureEffect(bufferOffset,
|
||||
effect.Parameter.Input[i],
|
||||
effect.State.SendBufferInfo,
|
||||
effect.IsEnabled,
|
||||
effect.Parameter.BufferStorageSize,
|
||||
effect.State.SendBufferInfoBase,
|
||||
updateCount,
|
||||
writeOffset,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateCaptureEffect(
|
||||
bufferOffset,
|
||||
effect.Parameter.Input[i],
|
||||
effect.State.SendBufferInfo,
|
||||
effect.IsEnabled,
|
||||
effect.Parameter.BufferStorageSize,
|
||||
effect.State.SendBufferInfoBase,
|
||||
updateCount,
|
||||
writeOffset,
|
||||
nodeId);
|
||||
|
||||
writeOffset = newUpdateCount;
|
||||
|
||||
@ -612,11 +739,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
Debug.Assert(effect.Type == EffectType.Compressor);
|
||||
|
||||
_commandBuffer.GenerateCompressorEffect(bufferOffset,
|
||||
effect.Parameter,
|
||||
effect.State,
|
||||
effect.IsEnabled,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateCompressorEffect(
|
||||
bufferOffset,
|
||||
effect.Parameter,
|
||||
effect.State,
|
||||
effect.IsEnabled,
|
||||
nodeId);
|
||||
}
|
||||
|
||||
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
|
||||
@ -629,8 +757,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
bool performanceInitialized = false;
|
||||
|
||||
if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(),
|
||||
isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId))
|
||||
if (_performanceManager != null && _performanceManager.GetNextEntry(
|
||||
out performanceEntry,
|
||||
effect.GetPerformanceDetailType(),
|
||||
isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix,
|
||||
nodeId))
|
||||
{
|
||||
performanceInitialized = true;
|
||||
|
||||
@ -706,6 +837,85 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateMixWithSplitter(
|
||||
uint inputBufferIndex,
|
||||
uint outputBufferIndex,
|
||||
float volume,
|
||||
SplitterDestination destination,
|
||||
ref bool isFirstMixBuffer,
|
||||
int nodeId)
|
||||
{
|
||||
ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0);
|
||||
ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1);
|
||||
|
||||
Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
|
||||
|
||||
if (bqf0.Enable && bqf1.Enable)
|
||||
{
|
||||
_commandBuffer.GenerateMultiTapBiquadFilterAndMix(
|
||||
0f,
|
||||
volume,
|
||||
inputBufferIndex,
|
||||
outputBufferIndex,
|
||||
0,
|
||||
Memory<VoiceUpdateState>.Empty,
|
||||
ref bqf0,
|
||||
ref bqf1,
|
||||
bqfState[..1],
|
||||
bqfState.Slice(1, 1),
|
||||
bqfState.Slice(2, 1),
|
||||
bqfState.Slice(3, 1),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
false,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
destination.UpdateBiquadFilterEnabledPrev(0);
|
||||
destination.UpdateBiquadFilterEnabledPrev(1);
|
||||
}
|
||||
else if (bqf0.Enable)
|
||||
{
|
||||
_commandBuffer.GenerateBiquadFilterAndMix(
|
||||
0f,
|
||||
volume,
|
||||
inputBufferIndex,
|
||||
outputBufferIndex,
|
||||
0,
|
||||
Memory<VoiceUpdateState>.Empty,
|
||||
ref bqf0,
|
||||
bqfState[..1],
|
||||
bqfState.Slice(1, 1),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
false,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
destination.UpdateBiquadFilterEnabledPrev(0);
|
||||
}
|
||||
else if (bqf1.Enable)
|
||||
{
|
||||
_commandBuffer.GenerateBiquadFilterAndMix(
|
||||
0f,
|
||||
volume,
|
||||
inputBufferIndex,
|
||||
outputBufferIndex,
|
||||
0,
|
||||
Memory<VoiceUpdateState>.Empty,
|
||||
ref bqf1,
|
||||
bqfState[..1],
|
||||
bqfState.Slice(1, 1),
|
||||
!destination.IsBiquadFilterEnabledPrev(),
|
||||
false,
|
||||
isFirstMixBuffer,
|
||||
nodeId);
|
||||
|
||||
destination.UpdateBiquadFilterEnabledPrev(1);
|
||||
}
|
||||
|
||||
isFirstMixBuffer = false;
|
||||
}
|
||||
|
||||
private void GenerateMix(ref MixState mix)
|
||||
{
|
||||
if (mix.HasAnyDestination())
|
||||
@ -722,15 +932,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
int destinationIndex = destinationId++;
|
||||
|
||||
Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
|
||||
SplitterDestination destination = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
|
||||
|
||||
if (destinationSpan.IsEmpty)
|
||||
if (destination.IsNull)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ref SplitterDestination destination = ref destinationSpan[0];
|
||||
|
||||
if (destination.IsConfigured())
|
||||
{
|
||||
int mixId = destination.DestinationId;
|
||||
@ -741,16 +949,32 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
|
||||
|
||||
bool isFirstMixBuffer = true;
|
||||
|
||||
for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
|
||||
{
|
||||
float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
|
||||
|
||||
if (volume != 0.0f)
|
||||
{
|
||||
_commandBuffer.GenerateMix(inputBufferIndex,
|
||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||
mix.NodeId,
|
||||
volume);
|
||||
if (destination.IsBiquadFilterEnabled())
|
||||
{
|
||||
GenerateMixWithSplitter(
|
||||
inputBufferIndex,
|
||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||
volume,
|
||||
destination,
|
||||
ref isFirstMixBuffer,
|
||||
mix.NodeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_commandBuffer.GenerateMix(
|
||||
inputBufferIndex,
|
||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||
mix.NodeId,
|
||||
volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -770,10 +994,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
if (volume != 0.0f)
|
||||
{
|
||||
_commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex,
|
||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||
mix.NodeId,
|
||||
volume);
|
||||
_commandBuffer.GenerateMix(
|
||||
mix.BufferOffset + bufferIndex,
|
||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||
mix.NodeId,
|
||||
volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -783,11 +1008,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
private void GenerateSubMix(ref MixState subMix)
|
||||
{
|
||||
_commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
|
||||
subMix.BufferOffset,
|
||||
subMix.BufferCount,
|
||||
subMix.NodeId,
|
||||
subMix.SampleRate);
|
||||
_commandBuffer.GenerateDepopForMixBuffers(
|
||||
_rendererContext.DepopBuffer,
|
||||
subMix.BufferOffset,
|
||||
subMix.BufferCount,
|
||||
subMix.NodeId,
|
||||
subMix.SampleRate);
|
||||
|
||||
GenerateEffects(ref subMix);
|
||||
|
||||
@ -847,11 +1073,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
ref MixState finalMix = ref _mixContext.GetFinalState();
|
||||
|
||||
_commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
|
||||
finalMix.BufferOffset,
|
||||
finalMix.BufferCount,
|
||||
finalMix.NodeId,
|
||||
finalMix.SampleRate);
|
||||
_commandBuffer.GenerateDepopForMixBuffers(
|
||||
_rendererContext.DepopBuffer,
|
||||
finalMix.BufferOffset,
|
||||
finalMix.BufferCount,
|
||||
finalMix.NodeId,
|
||||
finalMix.SampleRate);
|
||||
|
||||
GenerateEffects(ref finalMix);
|
||||
|
||||
@ -882,9 +1109,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
||||
}
|
||||
|
||||
_commandBuffer.GenerateVolume(finalMix.Volume,
|
||||
finalMix.BufferOffset + bufferIndex,
|
||||
nodeId);
|
||||
_commandBuffer.GenerateVolume(
|
||||
finalMix.Volume,
|
||||
finalMix.BufferOffset + bufferIndex,
|
||||
nodeId);
|
||||
|
||||
if (performanceSubInitialized)
|
||||
{
|
||||
@ -938,41 +1166,45 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
if (useCustomDownMixingCommand)
|
||||
{
|
||||
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
sink.DownMixCoefficients,
|
||||
Constants.InvalidNodeId);
|
||||
_commandBuffer.GenerateDownMixSurroundToStereo(
|
||||
finalMix.BufferOffset,
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
sink.DownMixCoefficients,
|
||||
Constants.InvalidNodeId);
|
||||
}
|
||||
// NOTE: We do the downmixing at the DSP level as it's easier that way.
|
||||
else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
|
||||
{
|
||||
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
Constants.DefaultSurroundToStereoCoefficients,
|
||||
Constants.InvalidNodeId);
|
||||
_commandBuffer.GenerateDownMixSurroundToStereo(
|
||||
finalMix.BufferOffset,
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
Constants.DefaultSurroundToStereoCoefficients,
|
||||
Constants.InvalidNodeId);
|
||||
}
|
||||
|
||||
CommandList commandList = _commandBuffer.CommandList;
|
||||
|
||||
if (sink.UpsamplerState != null)
|
||||
{
|
||||
_commandBuffer.GenerateUpsample(finalMix.BufferOffset,
|
||||
sink.UpsamplerState,
|
||||
sink.Parameter.InputCount,
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
commandList.BufferCount,
|
||||
commandList.SampleCount,
|
||||
commandList.SampleRate,
|
||||
Constants.InvalidNodeId);
|
||||
_commandBuffer.GenerateUpsample(
|
||||
finalMix.BufferOffset,
|
||||
sink.UpsamplerState,
|
||||
sink.Parameter.InputCount,
|
||||
sink.Parameter.Input.AsSpan(),
|
||||
commandList.BufferCount,
|
||||
commandList.SampleCount,
|
||||
commandList.SampleRate,
|
||||
Constants.InvalidNodeId);
|
||||
}
|
||||
|
||||
_commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
|
||||
sink,
|
||||
_rendererContext.SessionId,
|
||||
commandList.Buffers,
|
||||
Constants.InvalidNodeId);
|
||||
_commandBuffer.GenerateDeviceSink(
|
||||
finalMix.BufferOffset,
|
||||
sink,
|
||||
_rendererContext.SessionId,
|
||||
commandList.Buffers,
|
||||
Constants.InvalidNodeId);
|
||||
}
|
||||
|
||||
private void GenerateSink(BaseSink sink, ref MixState finalMix)
|
||||
|
@ -170,7 +170,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(GroupedBiquadFilterCommand command)
|
||||
public uint Estimate(MultiTapBiquadFilterCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -184,5 +184,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(BiquadFilterAndMixCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(MultiTapBiquadFilterAndMixCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,7 +462,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(GroupedBiquadFilterCommand command)
|
||||
public uint Estimate(MultiTapBiquadFilterCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -476,5 +476,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(BiquadFilterAndMixCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(MultiTapBiquadFilterAndMixCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +632,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
};
|
||||
}
|
||||
|
||||
public virtual uint Estimate(GroupedBiquadFilterCommand command)
|
||||
public virtual uint Estimate(MultiTapBiquadFilterCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -646,5 +646,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual uint Estimate(BiquadFilterAndMixCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual uint Estimate(MultiTapBiquadFilterAndMixCommand command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
public CommandProcessingTimeEstimatorVersion4(uint sampleCount, uint bufferCount) : base(sampleCount, bufferCount) { }
|
||||
|
||||
public override uint Estimate(GroupedBiquadFilterCommand command)
|
||||
public override uint Estimate(MultiTapBiquadFilterCommand command)
|
||||
{
|
||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
||||
|
||||
|
@ -210,5 +210,53 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
}
|
||||
|
||||
public override uint Estimate(BiquadFilterAndMixCommand command)
|
||||
{
|
||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
||||
|
||||
if (command.HasVolumeRamp)
|
||||
{
|
||||
if (SampleCount == 160)
|
||||
{
|
||||
return 5204;
|
||||
}
|
||||
|
||||
return 6683;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SampleCount == 160)
|
||||
{
|
||||
return 3427;
|
||||
}
|
||||
|
||||
return 4752;
|
||||
}
|
||||
}
|
||||
|
||||
public override uint Estimate(MultiTapBiquadFilterAndMixCommand command)
|
||||
{
|
||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
||||
|
||||
if (command.HasVolumeRamp)
|
||||
{
|
||||
if (SampleCount == 160)
|
||||
{
|
||||
return 7939;
|
||||
}
|
||||
|
||||
return 10669;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SampleCount == 160)
|
||||
{
|
||||
return 6256;
|
||||
}
|
||||
|
||||
return 8683;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,21 +33,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return WorkBuffers[index].GetReference(true);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
Parameter = MemoryMarshal.Cast<byte, AuxiliaryBufferParameter>(parameter.SpecificData)[0];
|
||||
IsEnabled = parameter.IsEnabled;
|
||||
|
@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
/// <returns>Returns true if the <see cref="EffectType"/> sent by the user matches the internal <see cref="EffectType"/>.</returns>
|
||||
public bool IsTypeValid<T>(ref T parameter) where T : unmanaged, IEffectInParameter
|
||||
public bool IsTypeValid<T>(in T parameter) where T : unmanaged, IEffectInParameter
|
||||
{
|
||||
return parameter.Type == TargetEffectType;
|
||||
}
|
||||
@ -98,7 +98,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
/// Update the internal common parameters from a user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
protected void UpdateParameterBase<T>(ref T parameter) where T : unmanaged, IEffectInParameter
|
||||
protected void UpdateParameterBase<T>(in T parameter) where T : unmanaged, IEffectInParameter
|
||||
{
|
||||
MixId = parameter.MixId;
|
||||
ProcessingOrder = parameter.ProcessingOrder;
|
||||
@ -139,7 +139,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
/// <summary>
|
||||
/// Initialize the given <paramref name="state"/> result state.
|
||||
/// </summary>
|
||||
/// <param name="state">The state to initalize</param>
|
||||
/// <param name="state">The state to initialize</param>
|
||||
public virtual void InitializeResultState(ref EffectResultState state) { }
|
||||
|
||||
/// <summary>
|
||||
@ -155,9 +155,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
/// <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, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public virtual void Update(out ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
updateErrorInfo = new ErrorInfo();
|
||||
}
|
||||
@ -168,9 +168,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
/// <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, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public virtual void Update(out ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
updateErrorInfo = new ErrorInfo();
|
||||
}
|
||||
|
@ -35,21 +35,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
|
||||
public override EffectType TargetEffectType => EffectType.BiquadFilter;
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
Parameter = MemoryMarshal.Cast<byte, BiquadFilterEffectParameter>(parameter.SpecificData)[0];
|
||||
IsEnabled = parameter.IsEnabled;
|
||||
|
@ -19,21 +19,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
|
||||
public override EffectType TargetEffectType => EffectType.BufferMix;
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
Parameter = MemoryMarshal.Cast<byte, BufferMixParameter>(parameter.SpecificData)[0];
|
||||
IsEnabled = parameter.IsEnabled;
|
||||
|
@ -32,21 +32,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return WorkBuffers[index].GetReference(true);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
Parameter = MemoryMarshal.Cast<byte, AuxiliaryBufferParameter>(parameter.SpecificData)[0];
|
||||
IsEnabled = parameter.IsEnabled;
|
||||
|
@ -39,17 +39,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
// Nintendo doesn't do anything here but we still require updateErrorInfo to be initialised.
|
||||
updateErrorInfo = new BehaviourParameter.ErrorInfo();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
Parameter = MemoryMarshal.Cast<byte, CompressorParameter>(parameter.SpecificData)[0];
|
||||
IsEnabled = parameter.IsEnabled;
|
||||
|
@ -37,19 +37,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
ref DelayParameter delayParameter = ref MemoryMarshal.Cast<byte, DelayParameter>(parameter.SpecificData)[0];
|
||||
|
||||
@ -57,7 +57,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
|
||||
if (delayParameter.IsChannelCountMaxValid())
|
||||
{
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
UsageState oldParameterStatus = Parameter.Status;
|
||||
|
||||
|
@ -39,25 +39,25 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
ref LimiterParameter limiterParameter = ref MemoryMarshal.Cast<byte, LimiterParameter>(parameter.SpecificData)[0];
|
||||
|
||||
updateErrorInfo = new BehaviourParameter.ErrorInfo();
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
Parameter = limiterParameter;
|
||||
|
||||
|
@ -36,19 +36,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
ref Reverb3dParameter reverbParameter = ref MemoryMarshal.Cast<byte, Reverb3dParameter>(parameter.SpecificData)[0];
|
||||
|
||||
@ -56,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
|
||||
if (reverbParameter.IsChannelCountMaxValid())
|
||||
{
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
UsageState oldParameterStatus = Parameter.ParameterStatus;
|
||||
|
||||
|
@ -39,19 +39,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Update(out updateErrorInfo, ref parameter, mapper);
|
||||
Update(out updateErrorInfo, in parameter, mapper);
|
||||
}
|
||||
|
||||
public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref 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
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
ref ReverbParameter reverbParameter = ref MemoryMarshal.Cast<byte, ReverbParameter>(parameter.SpecificData)[0];
|
||||
|
||||
@ -59,7 +59,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
|
||||
if (reverbParameter.IsChannelCountMaxValid())
|
||||
{
|
||||
UpdateParameterBase(ref parameter);
|
||||
UpdateParameterBase(in parameter);
|
||||
|
||||
UsageState oldParameterStatus = Parameter.Status;
|
||||
|
||||
|
@ -33,8 +33,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
uint Estimate(UpsampleCommand command);
|
||||
uint Estimate(LimiterCommandVersion1 command);
|
||||
uint Estimate(LimiterCommandVersion2 command);
|
||||
uint Estimate(GroupedBiquadFilterCommand command);
|
||||
uint Estimate(MultiTapBiquadFilterCommand command);
|
||||
uint Estimate(CaptureBufferCommand command);
|
||||
uint Estimate(CompressorCommand command);
|
||||
uint Estimate(BiquadFilterAndMixCommand command);
|
||||
uint Estimate(MultiTapBiquadFilterAndMixCommand command);
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
|
||||
/// <param name="inParameter">Input user parameter.</param>
|
||||
/// <param name="outStatus">Output user parameter.</param>
|
||||
/// <returns>Returns the <see cref="UpdateResult"/> of the operations performed.</returns>
|
||||
public UpdateResult Update(ref MemoryPoolState memoryPool, ref MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus)
|
||||
public UpdateResult Update(ref MemoryPoolState memoryPool, in MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus)
|
||||
{
|
||||
MemoryPoolUserState inputState = inParameter.State;
|
||||
|
||||
|
@ -195,7 +195,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
|
||||
/// <param name="parameter">The input parameter of the mix.</param>
|
||||
/// <param name="splitterContext">The splitter context.</param>
|
||||
/// <returns>Return true, new connections were done on the adjacency matrix.</returns>
|
||||
private bool UpdateConnection(EdgeMatrix edgeMatrix, ref MixParameter parameter, ref SplitterContext splitterContext)
|
||||
private bool UpdateConnection(EdgeMatrix edgeMatrix, in MixParameter parameter, ref SplitterContext splitterContext)
|
||||
{
|
||||
bool hasNewConnections;
|
||||
|
||||
@ -225,11 +225,11 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
|
||||
|
||||
for (int i = 0; i < splitter.DestinationCount; i++)
|
||||
{
|
||||
Span<SplitterDestination> destination = splitter.GetData(i);
|
||||
SplitterDestination destination = splitter.GetData(i);
|
||||
|
||||
if (!destination.IsEmpty)
|
||||
if (!destination.IsNull)
|
||||
{
|
||||
int destinationMixId = destination[0].DestinationId;
|
||||
int destinationMixId = destination.DestinationId;
|
||||
|
||||
if (destinationMixId != UnusedMixId)
|
||||
{
|
||||
@ -259,7 +259,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
|
||||
/// <param name="splitterContext">The splitter context.</param>
|
||||
/// <param name="behaviourContext">The behaviour context.</param>
|
||||
/// <returns>Return true if the mix was changed.</returns>
|
||||
public bool Update(EdgeMatrix edgeMatrix, ref MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext)
|
||||
public bool Update(EdgeMatrix edgeMatrix, in MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext)
|
||||
{
|
||||
bool isDirty;
|
||||
|
||||
@ -273,7 +273,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
|
||||
|
||||
if (behaviourContext.IsSplitterSupported())
|
||||
{
|
||||
isDirty = UpdateConnection(edgeMatrix, ref parameter, ref splitterContext);
|
||||
isDirty = UpdateConnection(edgeMatrix, in parameter, ref splitterContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -18,16 +18,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
|
||||
if (version == 2)
|
||||
{
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2,
|
||||
PerformanceEntryVersion2,
|
||||
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
}
|
||||
|
||||
if (version == 1)
|
||||
{
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1,
|
||||
PerformanceEntryVersion1,
|
||||
PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");
|
||||
|
@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
{
|
||||
performanceEntry = null;
|
||||
|
||||
if (_entryDetailIndex > MaxFrameDetailCount)
|
||||
if (_entryDetailIndex >= MaxFrameDetailCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
|
||||
};
|
||||
|
||||
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex);
|
||||
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
|
||||
|
||||
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];
|
||||
|
||||
|
@ -59,7 +59,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
/// <returns>Return true, if the <see cref="SinkType"/> sent by the user match the internal <see cref="SinkType"/>.</returns>
|
||||
public bool IsTypeValid(ref SinkInParameter parameter)
|
||||
public bool IsTypeValid(in SinkInParameter parameter)
|
||||
{
|
||||
return parameter.Type == TargetSinkType;
|
||||
}
|
||||
@ -76,7 +76,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
|
||||
/// Update the internal common parameters from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
protected void UpdateStandardParameter(ref SinkInParameter parameter)
|
||||
protected void UpdateStandardParameter(in SinkInParameter parameter)
|
||||
{
|
||||
if (IsUsed != parameter.IsUsed)
|
||||
{
|
||||
@ -92,9 +92,9 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
/// <param name="outStatus">The user output status.</param>
|
||||
/// <param name="mapper">The mapper to use.</param>
|
||||
public virtual void Update(out ErrorInfo errorInfo, ref SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
|
||||
public virtual void Update(out ErrorInfo errorInfo, in SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
errorInfo = new ErrorInfo();
|
||||
}
|
||||
|
@ -44,18 +44,18 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
|
||||
|
||||
public override SinkType TargetSinkType => SinkType.CircularBuffer;
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo errorInfo, ref SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo errorInfo, in SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
|
||||
{
|
||||
errorInfo = new BehaviourParameter.ErrorInfo();
|
||||
outStatus = new SinkOutStatus();
|
||||
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
ref CircularBufferParameter inputDeviceParameter = ref MemoryMarshal.Cast<byte, CircularBufferParameter>(parameter.SpecificData)[0];
|
||||
|
||||
if (parameter.IsUsed != IsUsed || ShouldSkip)
|
||||
{
|
||||
UpdateStandardParameter(ref parameter);
|
||||
UpdateStandardParameter(in parameter);
|
||||
|
||||
if (parameter.IsUsed)
|
||||
{
|
||||
|
@ -49,15 +49,15 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
|
||||
|
||||
public override SinkType TargetSinkType => SinkType.Device;
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo errorInfo, ref SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
|
||||
public override void Update(out BehaviourParameter.ErrorInfo errorInfo, in SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
Debug.Assert(IsTypeValid(in parameter));
|
||||
|
||||
ref DeviceParameter inputDeviceParameter = ref MemoryMarshal.Cast<byte, DeviceParameter>(parameter.SpecificData)[0];
|
||||
|
||||
if (parameter.IsUsed != IsUsed)
|
||||
{
|
||||
UpdateStandardParameter(ref parameter);
|
||||
UpdateStandardParameter(in parameter);
|
||||
Parameter = inputDeviceParameter;
|
||||
}
|
||||
else
|
||||
|
@ -1,11 +1,13 @@
|
||||
using Ryujinx.Audio.Renderer.Common;
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Audio.Renderer.Utils;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Extensions;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
{
|
||||
@ -14,18 +16,38 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
public class SplitterContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Amount of biquad filter states per splitter destination.
|
||||
/// </summary>
|
||||
public const int BqfStatesPerDestination = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Storage for <see cref="SplitterState"/>.
|
||||
/// </summary>
|
||||
private Memory<SplitterState> _splitters;
|
||||
|
||||
/// <summary>
|
||||
/// Storage for <see cref="SplitterDestination"/>.
|
||||
/// Storage for <see cref="SplitterDestinationVersion1"/>.
|
||||
/// </summary>
|
||||
private Memory<SplitterDestination> _splitterDestinations;
|
||||
private Memory<SplitterDestinationVersion1> _splitterDestinationsV1;
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, ref SplitterInParameter, ReadOnlySpan{byte})"/>.
|
||||
/// Storage for <see cref="SplitterDestinationVersion2"/>.
|
||||
/// </summary>
|
||||
private Memory<SplitterDestinationVersion2> _splitterDestinationsV2;
|
||||
|
||||
/// <summary>
|
||||
/// Splitter biquad filtering states.
|
||||
/// </summary>
|
||||
private Memory<BiquadFilterState> _splitterBqfStates;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the splitter context that is being used, currently can be 1 or 2.
|
||||
/// </summary>
|
||||
public int Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.
|
||||
/// </summary>
|
||||
public bool IsBugFixed { get; private set; }
|
||||
|
||||
@ -35,12 +57,17 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// <param name="behaviourContext">The behaviour context.</param>
|
||||
/// <param name="parameter">The audio renderer configuration.</param>
|
||||
/// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</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>
|
||||
public bool Initialize(ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator)
|
||||
public bool Initialize(
|
||||
ref BehaviourContext behaviourContext,
|
||||
ref AudioRendererConfiguration parameter,
|
||||
WorkBufferAllocator workBufferAllocator,
|
||||
Memory<BiquadFilterState> splitterBqfStates)
|
||||
{
|
||||
if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0)
|
||||
{
|
||||
Setup(Memory<SplitterState>.Empty, Memory<SplitterDestination>.Empty, false);
|
||||
Setup(Memory<SplitterState>.Empty, Memory<SplitterDestinationVersion1>.Empty, Memory<SplitterDestinationVersion2>.Empty, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -59,23 +86,62 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
splitter = new SplitterState(splitterId++);
|
||||
}
|
||||
|
||||
Memory<SplitterDestination> splitterDestinations = workBufferAllocator.Allocate<SplitterDestination>(parameter.SplitterDestinationCount,
|
||||
SplitterDestination.Alignment);
|
||||
Memory<SplitterDestinationVersion1> splitterDestinationsV1 = Memory<SplitterDestinationVersion1>.Empty;
|
||||
Memory<SplitterDestinationVersion2> splitterDestinationsV2 = Memory<SplitterDestinationVersion2>.Empty;
|
||||
|
||||
if (splitterDestinations.IsEmpty)
|
||||
if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled())
|
||||
{
|
||||
return false;
|
||||
Version = 1;
|
||||
|
||||
splitterDestinationsV1 = workBufferAllocator.Allocate<SplitterDestinationVersion1>(parameter.SplitterDestinationCount,
|
||||
SplitterDestinationVersion1.Alignment);
|
||||
|
||||
if (splitterDestinationsV1.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int splitterDestinationId = 0;
|
||||
foreach (ref SplitterDestinationVersion1 data in splitterDestinationsV1.Span)
|
||||
{
|
||||
data = new SplitterDestinationVersion1(splitterDestinationId++);
|
||||
}
|
||||
}
|
||||
|
||||
int splitterDestinationId = 0;
|
||||
foreach (ref SplitterDestination data in splitterDestinations.Span)
|
||||
else
|
||||
{
|
||||
data = new SplitterDestination(splitterDestinationId++);
|
||||
Version = 2;
|
||||
|
||||
splitterDestinationsV2 = workBufferAllocator.Allocate<SplitterDestinationVersion2>(parameter.SplitterDestinationCount,
|
||||
SplitterDestinationVersion2.Alignment);
|
||||
|
||||
if (splitterDestinationsV2.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int splitterDestinationId = 0;
|
||||
foreach (ref SplitterDestinationVersion2 data in splitterDestinationsV2.Span)
|
||||
{
|
||||
data = new SplitterDestinationVersion2(splitterDestinationId++);
|
||||
}
|
||||
|
||||
if (parameter.SplitterDestinationCount > 0)
|
||||
{
|
||||
// Official code stores it in the SplitterDestinationVersion2 struct,
|
||||
// but we don't to avoid using unsafe code.
|
||||
|
||||
splitterBqfStates.Span.Clear();
|
||||
_splitterBqfStates = splitterBqfStates;
|
||||
}
|
||||
else
|
||||
{
|
||||
_splitterBqfStates = Memory<BiquadFilterState>.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
SplitterState.InitializeSplitters(splitters.Span);
|
||||
|
||||
Setup(splitters, splitterDestinations, behaviourContext.IsSplitterBugFixed());
|
||||
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -92,7 +158,15 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
if (behaviourContext.IsSplitterSupported())
|
||||
{
|
||||
size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment);
|
||||
size = WorkBufferAllocator.GetTargetSize<SplitterDestination>(size, parameter.SplitterDestinationCount, SplitterDestination.Alignment);
|
||||
|
||||
if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled())
|
||||
{
|
||||
size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion2>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion1>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment);
|
||||
}
|
||||
|
||||
if (behaviourContext.IsSplitterBugFixed())
|
||||
{
|
||||
@ -109,12 +183,18 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// Setup the <see cref="SplitterContext"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="splitters">The <see cref="SplitterState"/> storage.</param>
|
||||
/// <param name="splitterDestinations">The <see cref="SplitterDestination"/> storage.</param>
|
||||
/// <param name="isBugFixed">If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, ref SplitterInParameter, ReadOnlySpan{byte})"/>.</param>
|
||||
private void Setup(Memory<SplitterState> splitters, Memory<SplitterDestination> splitterDestinations, bool isBugFixed)
|
||||
/// <param name="splitterDestinationsV1">The <see cref="SplitterDestinationVersion1"/> storage.</param>
|
||||
/// <param name="splitterDestinationsV2">The <see cref="SplitterDestinationVersion2"/> storage.</param>
|
||||
/// <param name="isBugFixed">If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.</param>
|
||||
private void Setup(
|
||||
Memory<SplitterState> splitters,
|
||||
Memory<SplitterDestinationVersion1> splitterDestinationsV1,
|
||||
Memory<SplitterDestinationVersion2> splitterDestinationsV2,
|
||||
bool isBugFixed)
|
||||
{
|
||||
_splitters = splitters;
|
||||
_splitterDestinations = splitterDestinations;
|
||||
_splitterDestinationsV1 = splitterDestinationsV1;
|
||||
_splitterDestinationsV2 = splitterDestinationsV2;
|
||||
IsBugFixed = isBugFixed;
|
||||
}
|
||||
|
||||
@ -140,7 +220,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _splitterDestinations.Length / _splitters.Length;
|
||||
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length;
|
||||
|
||||
return length / _splitters.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -148,11 +230,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
/// <param name="inputHeader">The splitter header.</param>
|
||||
/// <param name="input">The raw data after the splitter header.</param>
|
||||
private void UpdateState(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
|
||||
private void UpdateState(in SplitterInParameterHeader inputHeader, ref SequenceReader<byte> input)
|
||||
{
|
||||
for (int i = 0; i < inputHeader.SplitterCount; i++)
|
||||
{
|
||||
SplitterInParameter parameter = MemoryMarshal.Read<SplitterInParameter>(input);
|
||||
ref readonly SplitterInParameter parameter = ref input.GetRefOrRefToCopy<SplitterInParameter>(out _);
|
||||
|
||||
Debug.Assert(parameter.IsMagicValid());
|
||||
|
||||
@ -162,37 +244,78 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
{
|
||||
ref SplitterState splitter = ref GetState(parameter.Id);
|
||||
|
||||
splitter.Update(this, ref parameter, input[Unsafe.SizeOf<SplitterInParameter>()..]);
|
||||
splitter.Update(this, in parameter, ref input);
|
||||
}
|
||||
|
||||
input = input[(0x1C + parameter.DestinationCount * 4)..];
|
||||
// NOTE: there are 12 bytes of unused/unknown data after the destination IDs array.
|
||||
input.Advance(0xC);
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Rewind(Unsafe.SizeOf<SplitterInParameter>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update one or multiple <see cref="SplitterDestination"/> from user parameters.
|
||||
/// Update one splitter destination data from user parameters.
|
||||
/// </summary>
|
||||
/// <param name="input">The raw data after the splitter header.</param>
|
||||
/// <returns>True if the update was successful, false otherwise</returns>
|
||||
private bool UpdateData<T>(ref SequenceReader<byte> input) where T : unmanaged, ISplitterDestinationInParameter
|
||||
{
|
||||
ref readonly T parameter = ref input.GetRefOrRefToCopy<T>(out _);
|
||||
|
||||
Debug.Assert(parameter.IsMagicValid());
|
||||
|
||||
if (parameter.IsMagicValid())
|
||||
{
|
||||
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length;
|
||||
|
||||
if (parameter.Id >= 0 && parameter.Id < length)
|
||||
{
|
||||
SplitterDestination destination = GetDestination(parameter.Id);
|
||||
|
||||
destination.Update(parameter);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Rewind(Unsafe.SizeOf<T>());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update one or multiple splitter destination data from user parameters.
|
||||
/// </summary>
|
||||
/// <param name="inputHeader">The splitter header.</param>
|
||||
/// <param name="input">The raw data after the splitter header.</param>
|
||||
private void UpdateData(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
|
||||
private void UpdateData(in SplitterInParameterHeader inputHeader, ref SequenceReader<byte> input)
|
||||
{
|
||||
for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
|
||||
{
|
||||
SplitterDestinationInParameter parameter = MemoryMarshal.Read<SplitterDestinationInParameter>(input);
|
||||
|
||||
Debug.Assert(parameter.IsMagicValid());
|
||||
|
||||
if (parameter.IsMagicValid())
|
||||
if (Version == 1)
|
||||
{
|
||||
if (parameter.Id >= 0 && parameter.Id < _splitterDestinations.Length)
|
||||
if (!UpdateData<SplitterDestinationInParameterVersion1>(ref input))
|
||||
{
|
||||
ref SplitterDestination destination = ref GetDestination(parameter.Id);
|
||||
|
||||
destination.Update(parameter);
|
||||
break;
|
||||
}
|
||||
|
||||
input = input[Unsafe.SizeOf<SplitterDestinationInParameter>()..];
|
||||
}
|
||||
else if (Version == 2)
|
||||
{
|
||||
if (!UpdateData<SplitterDestinationInParameterVersion2>(ref input))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Fail($"Invalid splitter context version {Version}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,36 +324,33 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// Update splitter from user parameters.
|
||||
/// </summary>
|
||||
/// <param name="input">The input raw user data.</param>
|
||||
/// <param name="consumedSize">The total consumed size.</param>
|
||||
/// <returns>Return true if the update was successful.</returns>
|
||||
public bool Update(ReadOnlySpan<byte> input, out int consumedSize)
|
||||
public bool Update(ref SequenceReader<byte> input)
|
||||
{
|
||||
if (_splitterDestinations.IsEmpty || _splitters.IsEmpty)
|
||||
if (!UsingSplitter())
|
||||
{
|
||||
consumedSize = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int originalSize = input.Length;
|
||||
|
||||
SplitterInParameterHeader header = SpanIOHelper.Read<SplitterInParameterHeader>(ref input);
|
||||
ref readonly SplitterInParameterHeader header = ref input.GetRefOrRefToCopy<SplitterInParameterHeader>(out _);
|
||||
|
||||
if (header.IsMagicValid())
|
||||
{
|
||||
ClearAllNewConnectionFlag();
|
||||
|
||||
UpdateState(ref header, ref input);
|
||||
UpdateData(ref header, ref input);
|
||||
UpdateState(in header, ref input);
|
||||
UpdateData(in header, ref input);
|
||||
|
||||
consumedSize = BitUtils.AlignUp(originalSize - input.Length, 0x10);
|
||||
input.SetConsumed(BitUtils.AlignUp(input.Consumed, 0x10));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Rewind(Unsafe.SizeOf<SplitterInParameterHeader>());
|
||||
|
||||
consumedSize = 0;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -244,45 +364,52 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.
|
||||
/// Get a reference to the splitter destination data at the given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The index to use.</param>
|
||||
/// <returns>A reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.</returns>
|
||||
public ref SplitterDestination GetDestination(int id)
|
||||
/// <returns>A reference to the splitter destination data at the given <paramref name="id"/>.</returns>
|
||||
public SplitterDestination GetDestination(int id)
|
||||
{
|
||||
return ref SpanIOHelper.GetFromMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length);
|
||||
if (_splitterDestinationsV2.IsEmpty)
|
||||
{
|
||||
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a <see cref="Memory{SplitterDestination}"/> at the given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The index to use.</param>
|
||||
/// <returns>A <see cref="Memory{SplitterDestination}"/> at the given <paramref name="id"/>.</returns>
|
||||
public Memory<SplitterDestination> GetDestinationMemory(int id)
|
||||
{
|
||||
return SpanIOHelper.GetMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a <see cref="Span{SplitterDestination}"/> in the <see cref="SplitterState"/> at <paramref name="id"/> and pass <paramref name="destinationId"/> to <see cref="SplitterState.GetData(int)"/>.
|
||||
/// Get a <see cref="SplitterDestination"/> in the <see cref="SplitterState"/> at <paramref name="id"/> and pass <paramref name="destinationId"/> to <see cref="SplitterState.GetData(int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The index to use to get the <see cref="SplitterState"/>.</param>
|
||||
/// <param name="destinationId">The index of the <see cref="SplitterDestination"/>.</param>
|
||||
/// <returns>A <see cref="Span{SplitterDestination}"/>.</returns>
|
||||
public Span<SplitterDestination> GetDestination(int id, int destinationId)
|
||||
/// <returns>A <see cref="SplitterDestination"/>.</returns>
|
||||
public SplitterDestination GetDestination(int id, int destinationId)
|
||||
{
|
||||
ref SplitterState splitter = ref GetState(id);
|
||||
|
||||
return splitter.GetData(destinationId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the biquad filter state for a given splitter destination.
|
||||
/// </summary>
|
||||
/// <param name="destination">The splitter destination.</param>
|
||||
/// <returns>Biquad filter state for the specified destination.</returns>
|
||||
public Memory<BiquadFilterState> GetBiquadFilterState(SplitterDestination destination)
|
||||
{
|
||||
return _splitterBqfStates.Slice(destination.Id * BqfStatesPerDestination, BqfStatesPerDestination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the audio renderer has any splitters.
|
||||
/// </summary>
|
||||
/// <returns>True if the audio renderer has any splitters.</returns>
|
||||
public bool UsingSplitter()
|
||||
{
|
||||
return !_splitters.IsEmpty && !_splitterDestinations.IsEmpty;
|
||||
return !_splitters.IsEmpty && (!_splitterDestinationsV1.IsEmpty || !_splitterDestinationsV2.IsEmpty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,115 +1,198 @@
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Server state for a splitter destination.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)]
|
||||
public struct SplitterDestination
|
||||
public ref struct SplitterDestination
|
||||
{
|
||||
public const int Alignment = 0x10;
|
||||
private ref SplitterDestinationVersion1 _v1;
|
||||
private ref SplitterDestinationVersion2 _v2;
|
||||
|
||||
/// <summary>
|
||||
/// The unique id of this <see cref="SplitterDestination"/>.
|
||||
/// Checks if the splitter destination data reference is null.
|
||||
/// </summary>
|
||||
public int Id;
|
||||
public bool IsNull => Unsafe.IsNullRef(ref _v1) && Unsafe.IsNullRef(ref _v2);
|
||||
|
||||
/// <summary>
|
||||
/// The mix to output the result of the splitter.
|
||||
/// The splitter unique id.
|
||||
/// </summary>
|
||||
public int DestinationId;
|
||||
|
||||
/// <summary>
|
||||
/// Mix buffer volumes storage.
|
||||
/// </summary>
|
||||
private MixArray _mix;
|
||||
private MixArray _previousMix;
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the next linked element.
|
||||
/// </summary>
|
||||
private unsafe SplitterDestination* _next;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if in use.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the internal state need to be updated.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool NeedToUpdateInternalState;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 4 * 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 _mix);
|
||||
|
||||
/// <summary>
|
||||
/// Previous mix buffer volumes.
|
||||
/// </summary>
|
||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||
public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="Span{SplitterDestination}"/> of the next element or <see cref="Span{SplitterDestination}.Empty"/> if not present.
|
||||
/// </summary>
|
||||
public readonly Span<SplitterDestination> Next
|
||||
public int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
return _next != null ? new Span<SplitterDestination>(_next, 1) : Span<SplitterDestination>.Empty;
|
||||
if (Unsafe.IsNullRef(ref _v1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v1.Id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v2.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SplitterDestination"/>.
|
||||
/// The mix to output the result of the splitter.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of this <see cref="SplitterDestination"/>.</param>
|
||||
public SplitterDestination(int id) : this()
|
||||
public int DestinationId
|
||||
{
|
||||
Id = id;
|
||||
DestinationId = Constants.UnusedMixId;
|
||||
|
||||
ClearVolumes();
|
||||
get
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v1.DestinationId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v2.DestinationId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the <see cref="SplitterDestination"/> from user parameter.
|
||||
/// Mix buffer volumes.
|
||||
/// </summary>
|
||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||
public Span<float> MixBufferVolume
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v1))
|
||||
{
|
||||
return Span<float>.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v1.MixBufferVolume;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v2.MixBufferVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Previous mix buffer volumes.
|
||||
/// </summary>
|
||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||
public Span<float> PreviousMixBufferVolume
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v1))
|
||||
{
|
||||
return Span<float>.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v1.PreviousMixBufferVolume;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _v2.PreviousMixBufferVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="SplitterDestination"/> of the next element or null if not present.
|
||||
/// </summary>
|
||||
public readonly SplitterDestination Next
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v1))
|
||||
{
|
||||
return new SplitterDestination();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SplitterDestination(ref _v1.Next);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SplitterDestination(ref _v2.Next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new splitter destination wrapper for the version 1 splitter destination data.
|
||||
/// </summary>
|
||||
/// <param name="v1">Version 1 splitter destination data</param>
|
||||
public SplitterDestination(ref SplitterDestinationVersion1 v1)
|
||||
{
|
||||
_v1 = ref v1;
|
||||
_v2 = ref Unsafe.NullRef<SplitterDestinationVersion2>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new splitter destination wrapper for the version 2 splitter destination data.
|
||||
/// </summary>
|
||||
/// <param name="v2">Version 2 splitter destination data</param>
|
||||
public SplitterDestination(ref SplitterDestinationVersion2 v2)
|
||||
{
|
||||
|
||||
_v1 = ref Unsafe.NullRef<SplitterDestinationVersion1>();
|
||||
_v2 = ref v2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new splitter destination wrapper for the splitter destination data.
|
||||
/// </summary>
|
||||
/// <param name="v1">Version 1 splitter destination data</param>
|
||||
/// <param name="v2">Version 2 splitter destination data</param>
|
||||
public unsafe SplitterDestination(SplitterDestinationVersion1* v1, SplitterDestinationVersion2* v2)
|
||||
{
|
||||
_v1 = ref Unsafe.AsRef<SplitterDestinationVersion1>(v1);
|
||||
_v2 = ref Unsafe.AsRef<SplitterDestinationVersion2>(v2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the splitter destination data from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
public void Update(SplitterDestinationInParameter parameter)
|
||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||
{
|
||||
Debug.Assert(Id == parameter.Id);
|
||||
|
||||
if (parameter.IsMagicValid() && Id == parameter.Id)
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
DestinationId = parameter.DestinationId;
|
||||
|
||||
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||
|
||||
if (!IsUsed && parameter.IsUsed)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
|
||||
NeedToUpdateInternalState = false;
|
||||
}
|
||||
|
||||
IsUsed = parameter.IsUsed;
|
||||
_v1.Update(parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
_v2.Update(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,12 +201,14 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
public void UpdateInternalState()
|
||||
{
|
||||
if (IsUsed && NeedToUpdateInternalState)
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
_v1.UpdateInternalState();
|
||||
}
|
||||
else
|
||||
{
|
||||
_v2.UpdateInternalState();
|
||||
}
|
||||
|
||||
NeedToUpdateInternalState = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -131,16 +216,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
public void MarkAsNeedToUpdateInternalState()
|
||||
{
|
||||
NeedToUpdateInternalState = true;
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
_v1.MarkAsNeedToUpdateInternalState();
|
||||
}
|
||||
else
|
||||
{
|
||||
_v2.MarkAsNeedToUpdateInternalState();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the <see cref="SplitterDestination"/> is used and has a destination.
|
||||
/// Return true if the splitter destination is used and has a destination.
|
||||
/// </summary>
|
||||
/// <returns>True if the <see cref="SplitterDestination"/> is used and has a destination.</returns>
|
||||
/// <returns>True if the splitter destination is used and has a destination.</returns>
|
||||
public readonly bool IsConfigured()
|
||||
{
|
||||
return IsUsed && DestinationId != Constants.UnusedMixId;
|
||||
return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -150,9 +242,17 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// <returns>The volume for the given destination.</returns>
|
||||
public float GetMixVolume(int destinationIndex)
|
||||
{
|
||||
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
|
||||
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex);
|
||||
}
|
||||
|
||||
return MixBufferVolume[destinationIndex];
|
||||
/// <summary>
|
||||
/// Get the previous volume for a given destination.
|
||||
/// </summary>
|
||||
/// <param name="destinationIndex">The destination index to use.</param>
|
||||
/// <returns>The volume for the given destination.</returns>
|
||||
public float GetMixVolumePrev(int destinationIndex)
|
||||
{
|
||||
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -160,22 +260,33 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
public void ClearVolumes()
|
||||
{
|
||||
MixBufferVolume.Clear();
|
||||
PreviousMixBufferVolume.Clear();
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
_v1.ClearVolumes();
|
||||
}
|
||||
else
|
||||
{
|
||||
_v2.ClearVolumes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Link the next element to the given <see cref="SplitterDestination"/>.
|
||||
/// Link the next element to the given splitter destination.
|
||||
/// </summary>
|
||||
/// <param name="next">The given <see cref="SplitterDestination"/> to link.</param>
|
||||
public void Link(ref SplitterDestination next)
|
||||
/// <param name="next">The given splitter destination to link.</param>
|
||||
public void Link(SplitterDestination next)
|
||||
{
|
||||
unsafe
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
fixed (SplitterDestination* nextPtr = &next)
|
||||
{
|
||||
_next = nextPtr;
|
||||
}
|
||||
Debug.Assert(!Unsafe.IsNullRef(ref next._v1));
|
||||
|
||||
_v1.Link(ref next._v1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(!Unsafe.IsNullRef(ref next._v2));
|
||||
|
||||
_v2.Link(ref next._v2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,10 +295,74 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
public void Unlink()
|
||||
{
|
||||
unsafe
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
_next = null;
|
||||
_v1.Unlink();
|
||||
}
|
||||
else
|
||||
{
|
||||
_v2.Unlink();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any biquad filter is enabled.
|
||||
/// </summary>
|
||||
/// <returns>True if any biquad filter is enabled.</returns>
|
||||
public bool IsBiquadFilterEnabled()
|
||||
{
|
||||
return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any biquad filter was previously enabled.
|
||||
/// </summary>
|
||||
/// <returns>True if any biquad filter was previously enabled.</returns>
|
||||
public bool IsBiquadFilterEnabledPrev()
|
||||
{
|
||||
return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabledPrev();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the biquad filter parameters.
|
||||
/// </summary>
|
||||
/// <param name="index">Biquad filter index (0 or 1).</param>
|
||||
/// <returns>Biquad filter parameters.</returns>
|
||||
public ref BiquadFilterParameter GetBiquadFilterParameter(int index)
|
||||
{
|
||||
Debug.Assert(!Unsafe.IsNullRef(ref _v2));
|
||||
|
||||
return ref _v2.GetBiquadFilterParameter(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any biquad filter was previously enabled.
|
||||
/// </summary>
|
||||
/// <param name="index">Biquad filter index (0 or 1).</param>
|
||||
public void UpdateBiquadFilterEnabledPrev(int index)
|
||||
{
|
||||
if (!Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
_v2.UpdateBiquadFilterEnabledPrev(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the reference for the version 1 splitter destination data, or null if version 2 is being used or the destination is null.
|
||||
/// </summary>
|
||||
/// <returns>Reference for the version 1 splitter destination data.</returns>
|
||||
public ref SplitterDestinationVersion1 GetV1RefOrNull()
|
||||
{
|
||||
return ref _v1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the reference for the version 2 splitter destination data, or null if version 1 is being used or the destination is null.
|
||||
/// </summary>
|
||||
/// <returns>Reference for the version 2 splitter destination data.</returns>
|
||||
public ref SplitterDestinationVersion2 GetV2RefOrNull()
|
||||
{
|
||||
return ref _v2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,206 @@
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Server state for a splitter destination (version 1).
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)]
|
||||
public struct SplitterDestinationVersion1
|
||||
{
|
||||
public const int Alignment = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// The unique id of this <see cref="SplitterDestinationVersion1"/>.
|
||||
/// </summary>
|
||||
public int Id;
|
||||
|
||||
/// <summary>
|
||||
/// The mix to output the result of the splitter.
|
||||
/// </summary>
|
||||
public int DestinationId;
|
||||
|
||||
/// <summary>
|
||||
/// Mix buffer volumes storage.
|
||||
/// </summary>
|
||||
private MixArray _mix;
|
||||
private MixArray _previousMix;
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the next linked element.
|
||||
/// </summary>
|
||||
private unsafe SplitterDestinationVersion1* _next;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if in use.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the internal state need to be updated.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool NeedToUpdateInternalState;
|
||||
|
||||
[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 _mix);
|
||||
|
||||
/// <summary>
|
||||
/// Previous mix buffer volumes.
|
||||
/// </summary>
|
||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||
public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
|
||||
|
||||
/// <summary>
|
||||
/// Get the reference of the next element or null if not present.
|
||||
/// </summary>
|
||||
public readonly ref SplitterDestinationVersion1 Next
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return ref Unsafe.AsRef<SplitterDestinationVersion1>(_next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SplitterDestinationVersion1"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of this <see cref="SplitterDestinationVersion1"/>.</param>
|
||||
public SplitterDestinationVersion1(int id) : this()
|
||||
{
|
||||
Id = id;
|
||||
DestinationId = Constants.UnusedMixId;
|
||||
|
||||
ClearVolumes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||
{
|
||||
Debug.Assert(Id == parameter.Id);
|
||||
|
||||
if (parameter.IsMagicValid() && Id == parameter.Id)
|
||||
{
|
||||
DestinationId = parameter.DestinationId;
|
||||
|
||||
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||
|
||||
if (!IsUsed && parameter.IsUsed)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
|
||||
NeedToUpdateInternalState = false;
|
||||
}
|
||||
|
||||
IsUsed = parameter.IsUsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal state of the instance.
|
||||
/// </summary>
|
||||
public void UpdateInternalState()
|
||||
{
|
||||
if (IsUsed && NeedToUpdateInternalState)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
}
|
||||
|
||||
NeedToUpdateInternalState = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the update internal state marker.
|
||||
/// </summary>
|
||||
public void MarkAsNeedToUpdateInternalState()
|
||||
{
|
||||
NeedToUpdateInternalState = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the <see cref="SplitterDestinationVersion1"/> is used and has a destination.
|
||||
/// </summary>
|
||||
/// <returns>True if the <see cref="SplitterDestinationVersion1"/> is used and has a destination.</returns>
|
||||
public readonly bool IsConfigured()
|
||||
{
|
||||
return IsUsed && DestinationId != Constants.UnusedMixId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the volume for a given destination.
|
||||
/// </summary>
|
||||
/// <param name="destinationIndex">The destination index to use.</param>
|
||||
/// <returns>The volume for the given destination.</returns>
|
||||
public float GetMixVolume(int destinationIndex)
|
||||
{
|
||||
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
|
||||
|
||||
return MixBufferVolume[destinationIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the previous volume for a given destination.
|
||||
/// </summary>
|
||||
/// <param name="destinationIndex">The destination index to use.</param>
|
||||
/// <returns>The volume for the given destination.</returns>
|
||||
public float GetMixVolumePrev(int destinationIndex)
|
||||
{
|
||||
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
|
||||
|
||||
return PreviousMixBufferVolume[destinationIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the volumes.
|
||||
/// </summary>
|
||||
public void ClearVolumes()
|
||||
{
|
||||
MixBufferVolume.Clear();
|
||||
PreviousMixBufferVolume.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Link the next element to the given <see cref="SplitterDestinationVersion1"/>.
|
||||
/// </summary>
|
||||
/// <param name="next">The given <see cref="SplitterDestinationVersion1"/> to link.</param>
|
||||
public void Link(ref SplitterDestinationVersion1 next)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (SplitterDestinationVersion1* nextPtr = &next)
|
||||
{
|
||||
_next = nextPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the link to the next element.
|
||||
/// </summary>
|
||||
public void Unlink()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Server state for a splitter destination (version 2).
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x110, Pack = Alignment)]
|
||||
public struct SplitterDestinationVersion2
|
||||
{
|
||||
public const int Alignment = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// The unique id of this <see cref="SplitterDestinationVersion2"/>.
|
||||
/// </summary>
|
||||
public int Id;
|
||||
|
||||
/// <summary>
|
||||
/// The mix to output the result of the splitter.
|
||||
/// </summary>
|
||||
public int DestinationId;
|
||||
|
||||
/// <summary>
|
||||
/// Mix buffer volumes storage.
|
||||
/// </summary>
|
||||
private MixArray _mix;
|
||||
private MixArray _previousMix;
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the next linked element.
|
||||
/// </summary>
|
||||
private unsafe SplitterDestinationVersion2* _next;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if in use.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the internal state need to be updated.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool NeedToUpdateInternalState;
|
||||
|
||||
[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 _mix);
|
||||
|
||||
/// <summary>
|
||||
/// Previous mix buffer volumes.
|
||||
/// </summary>
|
||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||
public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
|
||||
|
||||
/// <summary>
|
||||
/// Get the reference of the next element or null if not present.
|
||||
/// </summary>
|
||||
public readonly ref SplitterDestinationVersion2 Next
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return ref Unsafe.AsRef<SplitterDestinationVersion2>(_next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Array2<BiquadFilterParameter> _biquadFilters;
|
||||
|
||||
private Array2<bool> _isPreviousBiquadFilterEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SplitterDestinationVersion2"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of this <see cref="SplitterDestinationVersion2"/>.</param>
|
||||
public SplitterDestinationVersion2(int id) : this()
|
||||
{
|
||||
Id = id;
|
||||
DestinationId = Constants.UnusedMixId;
|
||||
|
||||
ClearVolumes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||
{
|
||||
Debug.Assert(Id == parameter.Id);
|
||||
|
||||
if (parameter.IsMagicValid() && Id == parameter.Id)
|
||||
{
|
||||
DestinationId = parameter.DestinationId;
|
||||
|
||||
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||
|
||||
_biquadFilters = parameter.BiquadFilters;
|
||||
|
||||
if (!IsUsed && parameter.IsUsed)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
|
||||
NeedToUpdateInternalState = false;
|
||||
}
|
||||
|
||||
IsUsed = parameter.IsUsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal state of the instance.
|
||||
/// </summary>
|
||||
public void UpdateInternalState()
|
||||
{
|
||||
if (IsUsed && NeedToUpdateInternalState)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
}
|
||||
|
||||
NeedToUpdateInternalState = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the update internal state marker.
|
||||
/// </summary>
|
||||
public void MarkAsNeedToUpdateInternalState()
|
||||
{
|
||||
NeedToUpdateInternalState = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the <see cref="SplitterDestinationVersion2"/> is used and has a destination.
|
||||
/// </summary>
|
||||
/// <returns>True if the <see cref="SplitterDestinationVersion2"/> is used and has a destination.</returns>
|
||||
public readonly bool IsConfigured()
|
||||
{
|
||||
return IsUsed && DestinationId != Constants.UnusedMixId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the volume for a given destination.
|
||||
/// </summary>
|
||||
/// <param name="destinationIndex">The destination index to use.</param>
|
||||
/// <returns>The volume for the given destination.</returns>
|
||||
public float GetMixVolume(int destinationIndex)
|
||||
{
|
||||
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
|
||||
|
||||
return MixBufferVolume[destinationIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the previous volume for a given destination.
|
||||
/// </summary>
|
||||
/// <param name="destinationIndex">The destination index to use.</param>
|
||||
/// <returns>The volume for the given destination.</returns>
|
||||
public float GetMixVolumePrev(int destinationIndex)
|
||||
{
|
||||
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
|
||||
|
||||
return PreviousMixBufferVolume[destinationIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the volumes.
|
||||
/// </summary>
|
||||
public void ClearVolumes()
|
||||
{
|
||||
MixBufferVolume.Clear();
|
||||
PreviousMixBufferVolume.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Link the next element to the given <see cref="SplitterDestinationVersion2"/>.
|
||||
/// </summary>
|
||||
/// <param name="next">The given <see cref="SplitterDestinationVersion2"/> to link.</param>
|
||||
public void Link(ref SplitterDestinationVersion2 next)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (SplitterDestinationVersion2* nextPtr = &next)
|
||||
{
|
||||
_next = nextPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the link to the next element.
|
||||
/// </summary>
|
||||
public void Unlink()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_next = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any biquad filter is enabled.
|
||||
/// </summary>
|
||||
/// <returns>True if any biquad filter is enabled.</returns>
|
||||
public bool IsBiquadFilterEnabled()
|
||||
{
|
||||
return _biquadFilters[0].Enable || _biquadFilters[1].Enable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any biquad filter was previously enabled.
|
||||
/// </summary>
|
||||
/// <returns>True if any biquad filter was previously enabled.</returns>
|
||||
public bool IsBiquadFilterEnabledPrev()
|
||||
{
|
||||
return _isPreviousBiquadFilterEnabled[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the biquad filter parameters.
|
||||
/// </summary>
|
||||
/// <param name="index">Biquad filter index (0 or 1).</param>
|
||||
/// <returns>Biquad filter parameters.</returns>
|
||||
public ref BiquadFilterParameter GetBiquadFilterParameter(int index)
|
||||
{
|
||||
return ref _biquadFilters[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any biquad filter was previously enabled.
|
||||
/// </summary>
|
||||
/// <param name="index">Biquad filter index (0 or 1).</param>
|
||||
public void UpdateBiquadFilterEnabledPrev(int index)
|
||||
{
|
||||
_isPreviousBiquadFilterEnabled[index] = _biquadFilters[index].Enable;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user