Compare commits
2 commits
master
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
b51d57b642 | ||
|
54c16f4052 |
410 changed files with 5021 additions and 14545 deletions
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -23,7 +23,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Log file
|
label: Log file
|
||||||
description: A log file will help our developers to better diagnose and fix the issue.
|
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. 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).
|
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
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
|
|
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -1,7 +1,6 @@
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest a new feature for Ryujinx.
|
description: Suggest a new feature for Ryujinx.
|
||||||
title: "[Feature Request]"
|
title: "[Feature Request]"
|
||||||
labels: enhancement
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: overview
|
id: overview
|
||||||
|
|
4
.github/workflows/nightly_pr_comment.yml
vendored
4
.github/workflows/nightly_pr_comment.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const {owner, repo} = context.repo;
|
const {owner, repo} = context.repo;
|
||||||
|
@ -19,7 +19,7 @@ jobs:
|
||||||
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
||||||
|
|
||||||
const issue_number = await (async () => {
|
const issue_number = await (async () => {
|
||||||
const pulls = await github.rest.pulls.list({owner, repo});
|
const pulls = await github.rest.pulls.list.endpoint.merge({owner, repo});
|
||||||
for await (const {data} of github.paginate.iterator(pulls)) {
|
for await (const {data} of github.paginate.iterator(pulls)) {
|
||||||
for (const pull of data) {
|
for (const pull of data) {
|
||||||
if (pull.head.sha === pull_head_sha) {
|
if (pull.head.sha === pull_head_sha) {
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -34,7 +34,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create tag
|
- name: Create tag
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
|
|
|
@ -8,21 +8,21 @@
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" 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.Markup.Xaml.Loader" Version="11.0.10" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.16" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.16" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
<PackageVersion Include="DynamicData" Version="8.4.1" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.1" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
|
@ -39,11 +39,11 @@
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||||
|
|
|
@ -36,8 +36,8 @@
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
|
As of October 2023, Ryujinx has been tested on approximately 4,200 titles;
|
||||||
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
|
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable.
|
||||||
|
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||||
|
|
||||||
|
|
|
@ -87,8 +87,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
|
||||||
EndProject
|
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}"
|
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
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -251,10 +249,6 @@ Global
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
<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/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/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/=ASET/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@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>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|
|
@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||||
long originalPosition = _stream.Position;
|
long originalPosition = _stream.Position;
|
||||||
|
|
||||||
_stream.Seek(0, SeekOrigin.Begin);
|
_stream.Seek(0, SeekOrigin.Begin);
|
||||||
_stream.ReadExactly(code, 0, code.Length);
|
_stream.Read(code, 0, code.Length);
|
||||||
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
RelocInfo relocInfo;
|
RelocInfo relocInfo;
|
||||||
|
|
|
@ -251,20 +251,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a copy destination variable, we prefer the register used for the copy source.
|
int selectedReg = GetHighestValueIndex(freePositions);
|
||||||
// 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];
|
int selectedNextUse = freePositions[selectedReg];
|
||||||
|
|
||||||
// Intervals starts and ends at odd positions, unless they span an entire
|
// Intervals starts and ends at odd positions, unless they span an entire
|
||||||
|
@ -444,7 +431,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetHighestValueIndex(ReadOnlySpan<int> span)
|
private static int GetHighestValueIndex(Span<int> span)
|
||||||
{
|
{
|
||||||
int highest = int.MinValue;
|
int highest = int.MinValue;
|
||||||
|
|
||||||
|
@ -811,12 +798,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
// The "visited" state is stored in the MSB of the local's value.
|
// The "visited" state is stored in the MSB of the local's value.
|
||||||
const ulong VisitedMask = 1ul << 63;
|
const ulong VisitedMask = 1ul << 63;
|
||||||
|
|
||||||
static bool IsVisited(Operand local)
|
bool IsVisited(Operand local)
|
||||||
{
|
{
|
||||||
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetVisited(Operand local)
|
void SetVisited(Operand local)
|
||||||
{
|
{
|
||||||
local.GetValueUnsafe() |= VisitedMask;
|
local.GetValueUnsafe() |= VisitedMask;
|
||||||
}
|
}
|
||||||
|
@ -839,25 +826,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
{
|
{
|
||||||
dest.NumberLocal(_intervals.Count);
|
dest.NumberLocal(_intervals.Count);
|
||||||
|
|
||||||
LiveInterval interval = new LiveInterval(dest);
|
_intervals.Add(new LiveInterval(dest));
|
||||||
_intervals.Add(interval);
|
|
||||||
|
|
||||||
SetVisited(dest);
|
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,7 +19,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
public LiveRange CurrRange;
|
public LiveRange CurrRange;
|
||||||
|
|
||||||
public LiveInterval Parent;
|
public LiveInterval Parent;
|
||||||
public LiveInterval CopySource;
|
|
||||||
|
|
||||||
public UseList Uses;
|
public UseList Uses;
|
||||||
public LiveIntervalList Children;
|
public LiveIntervalList Children;
|
||||||
|
@ -38,7 +37,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
private ref LiveRange CurrRange => ref _data->CurrRange;
|
private ref LiveRange CurrRange => ref _data->CurrRange;
|
||||||
private ref LiveRange PrevRange => ref _data->PrevRange;
|
private ref LiveRange PrevRange => ref _data->PrevRange;
|
||||||
private ref LiveInterval Parent => ref _data->Parent;
|
private ref LiveInterval Parent => ref _data->Parent;
|
||||||
private ref LiveInterval CopySource => ref _data->CopySource;
|
|
||||||
private ref UseList Uses => ref _data->Uses;
|
private ref UseList Uses => ref _data->Uses;
|
||||||
private ref LiveIntervalList Children => ref _data->Children;
|
private ref LiveIntervalList Children => ref _data->Children;
|
||||||
|
|
||||||
|
@ -80,25 +78,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
Register = register;
|
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()
|
public void Reset()
|
||||||
{
|
{
|
||||||
PrevRange = default;
|
PrevRange = default;
|
||||||
|
|
|
@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
|
|
||||||
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||||
|
|
||||||
_stream.ReadExactly(buffer);
|
_stream.Read(buffer);
|
||||||
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
||||||
|
|
||||||
codeStream.Write(buffer);
|
codeStream.Write(buffer);
|
||||||
|
|
|
@ -746,7 +746,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
||||||
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||||
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", 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("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
||||||
|
@ -823,10 +822,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, 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("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
||||||
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
||||||
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
||||||
|
@ -1012,8 +1007,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, 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("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, 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);
|
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
|
@ -1035,10 +1028,8 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.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("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("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.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("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||||
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||||
|
@ -1063,7 +1054,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
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<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
||||||
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.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("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
|
|
@ -2,8 +2,6 @@ using ARMeilleure.Decoders;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||||
|
@ -292,16 +290,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitAluStore(context, res);
|
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)
|
public static void Rbit(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
Operand m = GetAluM(context);
|
Operand m = GetAluM(context);
|
||||||
|
@ -570,46 +558,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitHsub8(context, unsigned: true);
|
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)
|
public static void Usat(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||||
|
@ -986,251 +934,6 @@ 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)
|
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||||
{
|
{
|
||||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||||
|
|
|
@ -1246,33 +1246,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
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)
|
public static void Vqsub(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||||
|
|
|
@ -191,26 +191,6 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
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)
|
public static void Vtbl(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
||||||
|
|
|
@ -106,38 +106,6 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
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)
|
public static void Vshr(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
@ -162,36 +130,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
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)
|
public static void Vsra(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
|
|
@ -527,7 +527,6 @@ namespace ARMeilleure.Instructions
|
||||||
Pld,
|
Pld,
|
||||||
Pop,
|
Pop,
|
||||||
Push,
|
Push,
|
||||||
Qadd16,
|
|
||||||
Rev,
|
Rev,
|
||||||
Revsh,
|
Revsh,
|
||||||
Rsb,
|
Rsb,
|
||||||
|
@ -572,10 +571,6 @@ namespace ARMeilleure.Instructions
|
||||||
Umaal,
|
Umaal,
|
||||||
Umlal,
|
Umlal,
|
||||||
Umull,
|
Umull,
|
||||||
Uqadd16,
|
|
||||||
Uqadd8,
|
|
||||||
Uqsub16,
|
|
||||||
Uqsub8,
|
|
||||||
Usat,
|
Usat,
|
||||||
Usat16,
|
Usat16,
|
||||||
Usub8,
|
Usub8,
|
||||||
|
@ -650,7 +645,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vqdmulh,
|
Vqdmulh,
|
||||||
Vqmovn,
|
Vqmovn,
|
||||||
Vqmovun,
|
Vqmovun,
|
||||||
Vqrdmulh,
|
|
||||||
Vqrshrn,
|
Vqrshrn,
|
||||||
Vqrshrun,
|
Vqrshrun,
|
||||||
Vqshrn,
|
Vqshrn,
|
||||||
|
@ -672,7 +666,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vshll,
|
Vshll,
|
||||||
Vshr,
|
Vshr,
|
||||||
Vshrn,
|
Vshrn,
|
||||||
Vsli,
|
|
||||||
Vst1,
|
Vst1,
|
||||||
Vst2,
|
Vst2,
|
||||||
Vst3,
|
Vst3,
|
||||||
|
@ -689,7 +682,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vsub,
|
Vsub,
|
||||||
Vsubl,
|
Vsubl,
|
||||||
Vsubw,
|
Vsubw,
|
||||||
Vswp,
|
|
||||||
Vtbl,
|
Vtbl,
|
||||||
Vtrn,
|
Vtrn,
|
||||||
Vtst,
|
Vtst,
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
|
||||||
private int[] _postOrderMap;
|
private int[] _postOrderMap;
|
||||||
|
|
||||||
public int LocalsCount { get; private set; }
|
public int LocalsCount { get; private set; }
|
||||||
public BasicBlock Entry { get; private set; }
|
public BasicBlock Entry { get; }
|
||||||
public IntrusiveList<BasicBlock> Blocks { get; }
|
public IntrusiveList<BasicBlock> Blocks { get; }
|
||||||
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
||||||
public int[] PostOrderMap => _postOrderMap;
|
public int[] PostOrderMap => _postOrderMap;
|
||||||
|
@ -34,15 +34,6 @@ namespace ARMeilleure.Translation
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEntry(BasicBlock newEntry)
|
|
||||||
{
|
|
||||||
newEntry.AddSuccessor(Entry);
|
|
||||||
|
|
||||||
Entry = newEntry;
|
|
||||||
Blocks.AddFirst(newEntry);
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
RemoveUnreachableBlocks(Blocks);
|
RemoveUnreachableBlocks(Blocks);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
|
@ -10,10 +11,11 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
public IntPtr FuncPtr { get; }
|
public IntPtr FuncPtr { get; }
|
||||||
|
|
||||||
public DelegateInfo(Delegate dlg, IntPtr funcPtr)
|
public DelegateInfo(Delegate dlg)
|
||||||
{
|
{
|
||||||
_dlg = dlg;
|
_dlg = dlg;
|
||||||
FuncPtr = funcPtr;
|
|
||||||
|
FuncPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(dlg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ using ARMeilleure.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
|
@ -65,11 +64,11 @@ namespace ARMeilleure.Translation
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr)
|
private static void SetDelegateInfo(Delegate dlg)
|
||||||
{
|
{
|
||||||
string key = GetKey(dlg.Method);
|
string key = GetKey(dlg.Method);
|
||||||
|
|
||||||
_delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
|
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetKey(MethodInfo info)
|
private static string GetKey(MethodInfo info)
|
||||||
|
@ -83,353 +82,179 @@ namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
_delegates = new SortedList<string, DelegateInfo>();
|
_delegates = new SortedList<string, DelegateInfo>();
|
||||||
|
|
||||||
var dlgMathAbs = new MathAbs(Math.Abs);
|
SetDelegateInfo(new MathAbs(Math.Abs));
|
||||||
var dlgMathCeiling = new MathCeiling(Math.Ceiling);
|
SetDelegateInfo(new MathCeiling(Math.Ceiling));
|
||||||
var dlgMathFloor = new MathFloor(Math.Floor);
|
SetDelegateInfo(new MathFloor(Math.Floor));
|
||||||
var dlgMathRound = new MathRound(Math.Round);
|
SetDelegateInfo(new MathRound(Math.Round));
|
||||||
var dlgMathTruncate = new MathTruncate(Math.Truncate);
|
SetDelegateInfo(new MathTruncate(Math.Truncate));
|
||||||
|
|
||||||
var dlgMathFAbs = new MathFAbs(MathF.Abs);
|
SetDelegateInfo(new MathFAbs(MathF.Abs));
|
||||||
var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling);
|
SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
|
||||||
var dlgMathFFloor = new MathFFloor(MathF.Floor);
|
SetDelegateInfo(new MathFFloor(MathF.Floor));
|
||||||
var dlgMathFRound = new MathFRound(MathF.Round);
|
SetDelegateInfo(new MathFRound(MathF.Round));
|
||||||
var dlgMathFTruncate = new MathFTruncate(MathF.Truncate);
|
SetDelegateInfo(new MathFTruncate(MathF.Truncate));
|
||||||
|
|
||||||
var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break);
|
SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
|
||||||
var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization);
|
SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
|
||||||
var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit);
|
SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
|
||||||
var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0);
|
SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
|
||||||
var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0);
|
SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
|
||||||
var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0);
|
SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
|
||||||
var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0);
|
SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
|
||||||
var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0);
|
SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
|
||||||
var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress);
|
SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
|
||||||
var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine);
|
SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
|
||||||
var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte);
|
SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
|
||||||
var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16);
|
SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
|
||||||
var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32);
|
SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
|
||||||
var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64);
|
SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
|
||||||
var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128);
|
SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
|
||||||
var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking);
|
SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
|
||||||
var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall);
|
SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
|
||||||
var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess);
|
SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
|
||||||
var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined);
|
SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
|
||||||
var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte);
|
SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
|
||||||
var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16);
|
SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
|
||||||
var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32);
|
SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
|
||||||
var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64);
|
SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
|
||||||
var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128);
|
SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
|
||||||
|
|
||||||
var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns);
|
SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
|
||||||
var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros);
|
SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
|
||||||
var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b);
|
SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
|
||||||
var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb);
|
SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
|
||||||
var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch);
|
SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
|
||||||
var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw);
|
SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
|
||||||
var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx);
|
SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
|
||||||
var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h);
|
SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
|
||||||
var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w);
|
SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
|
||||||
var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x);
|
SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
|
||||||
var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt);
|
SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
|
||||||
var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt);
|
SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
|
||||||
var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate);
|
SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
|
||||||
var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose);
|
SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
|
||||||
var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower);
|
SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
|
||||||
var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority);
|
SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
|
||||||
var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity);
|
SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
|
||||||
var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper);
|
SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
|
||||||
var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns);
|
SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
|
||||||
var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns);
|
SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
|
||||||
var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128);
|
SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
|
||||||
var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32);
|
SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
|
||||||
var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64);
|
SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
|
||||||
var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32);
|
SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
|
||||||
var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64);
|
SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
|
||||||
var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32);
|
SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
|
||||||
var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64);
|
SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
|
||||||
var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32);
|
SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
|
||||||
var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64);
|
SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
|
||||||
var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1);
|
SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
|
||||||
var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2);
|
SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
|
||||||
var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1);
|
SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
|
||||||
var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2);
|
SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
|
||||||
var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64);
|
SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
|
||||||
var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1);
|
SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
|
||||||
var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2);
|
SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
|
||||||
var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3);
|
SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
|
||||||
var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4);
|
SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
|
||||||
var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1);
|
SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
|
||||||
var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2);
|
SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
|
||||||
var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3);
|
SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
|
||||||
var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4);
|
SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
|
||||||
var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64);
|
SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
|
||||||
|
|
||||||
var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert);
|
SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
|
||||||
var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert);
|
SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
|
||||||
|
|
||||||
var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd);
|
SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
|
||||||
var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare);
|
SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
|
||||||
var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ);
|
SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
|
||||||
var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE);
|
SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
|
||||||
var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT);
|
SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
|
||||||
var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE);
|
SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
|
||||||
var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT);
|
SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
|
||||||
var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv);
|
SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
|
||||||
var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax);
|
SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
|
||||||
var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum);
|
SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
|
||||||
var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin);
|
SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
|
||||||
var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum);
|
SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
|
||||||
var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul);
|
SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
|
||||||
var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd);
|
SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
|
||||||
var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub);
|
SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
|
||||||
var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX);
|
SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
|
||||||
var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd);
|
SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
|
||||||
var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub);
|
SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
|
||||||
var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate);
|
SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
|
||||||
var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
|
||||||
var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused);
|
SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
|
||||||
var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX);
|
SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
|
||||||
var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate);
|
SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
|
||||||
var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
|
||||||
var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only.
|
SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
|
||||||
var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused);
|
SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
|
||||||
var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt);
|
SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
|
||||||
var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub);
|
SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
|
||||||
|
|
||||||
var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert);
|
SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
|
||||||
|
|
||||||
var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd);
|
SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
|
||||||
var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare);
|
SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
|
||||||
var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ);
|
SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
|
||||||
var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE);
|
SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
|
||||||
var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT);
|
SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
|
||||||
var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE);
|
SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
|
||||||
var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT);
|
SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
|
||||||
var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv);
|
SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
|
||||||
var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax);
|
SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
|
||||||
var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum);
|
SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
|
||||||
var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin);
|
SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
|
||||||
var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum);
|
SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
|
||||||
var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul);
|
SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
|
||||||
var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd);
|
SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
|
||||||
var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub);
|
SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
|
||||||
var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX);
|
SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
|
||||||
var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd);
|
SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
|
||||||
var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub);
|
SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
|
||||||
var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate);
|
SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
|
||||||
var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
|
||||||
var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused);
|
SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
|
||||||
var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX);
|
SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
|
||||||
var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate);
|
SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
|
||||||
var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
|
||||||
var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only.
|
SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
|
||||||
var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused);
|
SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
|
||||||
var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt);
|
SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
|
||||||
var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub);
|
SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
|
||||||
|
|
||||||
var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert);
|
SetDelegateInfo(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);
|
private delegate double MathAbs(double value);
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 6634; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -857,14 +857,8 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
foreach (var thread in threads)
|
threads.ForEach((thread) => thread.Start());
|
||||||
{
|
threads.ForEach((thread) => thread.Join());
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
foreach (var thread in threads)
|
|
||||||
{
|
|
||||||
thread.Join();
|
|
||||||
}
|
|
||||||
|
|
||||||
threads.Clear();
|
threads.Clear();
|
||||||
|
|
||||||
|
|
|
@ -89,17 +89,6 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
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.
|
// Compute local register inputs and outputs used inside blocks.
|
||||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
||||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||||
|
@ -212,7 +201,7 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
// The only block without any predecessor should be the entry block.
|
// 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.
|
// It always needs a context load as it is the first block to run.
|
||||||
if (block == cfg.Entry || hasContextLoad)
|
if (block.Predecessors.Count == 0 || hasContextLoad)
|
||||||
{
|
{
|
||||||
long vecMask = globalInputs[block.Index].VecMask;
|
long vecMask = globalInputs[block.Index].VecMask;
|
||||||
long intMask = globalInputs[block.Index].IntMask;
|
long intMask = globalInputs[block.Index].IntMask;
|
||||||
|
|
|
@ -80,10 +80,7 @@ namespace ARMeilleure.Translation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_disposed)
|
Monitor.Wait(Sync);
|
||||||
{
|
|
||||||
Monitor.Wait(Sync);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,9 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * _bytesPerFrame);
|
using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * _bytesPerFrame);
|
||||||
|
|
||||||
Span<byte> samples = samplesOwner.Span;
|
Span<byte> samples = samplesOwner.Memory.Span;
|
||||||
|
|
||||||
_ringBuffer.Read(samples, 0, samples.Length);
|
_ringBuffer.Read(samples, 0, samples.Length);
|
||||||
|
|
||||||
|
|
|
@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
|
|
||||||
int channelCount = areas.Length;
|
int channelCount = areas.Length;
|
||||||
|
|
||||||
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * bytesPerFrame);
|
using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * bytesPerFrame);
|
||||||
|
|
||||||
Span<byte> samples = samplesOwner.Span;
|
Span<byte> samples = samplesOwner.Memory.Span;
|
||||||
|
|
||||||
_ringBuffer.Read(samples, 0, samples.Length);
|
_ringBuffer.Read(samples, 0, samples.Length);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Backends.Common
|
||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
|
|
||||||
private MemoryOwner<byte> _bufferOwner;
|
private IMemoryOwner<byte> _bufferOwner;
|
||||||
private Memory<byte> _buffer;
|
private Memory<byte> _buffer;
|
||||||
private int _size;
|
private int _size;
|
||||||
private int _headOffset;
|
private int _headOffset;
|
||||||
|
@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Backends.Common
|
||||||
|
|
||||||
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
|
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
|
||||||
{
|
{
|
||||||
_bufferOwner = MemoryOwner<byte>.RentCleared(initialCapacity);
|
_bufferOwner = ByteMemoryPool.RentCleared(initialCapacity);
|
||||||
_buffer = _bufferOwner.Memory;
|
_buffer = _bufferOwner.Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Backends.Common
|
||||||
|
|
||||||
private void SetCapacityLocked(int capacity)
|
private void SetCapacityLocked(int capacity)
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> newBufferOwner = MemoryOwner<byte>.RentCleared(capacity);
|
IMemoryOwner<byte> newBufferOwner = ByteMemoryPool.RentCleared(capacity);
|
||||||
Memory<byte> newBuffer = newBufferOwner.Memory;
|
Memory<byte> newBuffer = newBufferOwner.Memory;
|
||||||
|
|
||||||
if (_size > 0)
|
if (_size > 0)
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||||
{
|
{
|
||||||
public const int Align = 0x10;
|
public const int Align = 0x10;
|
||||||
public const int BiquadStateOffset = 0x0;
|
public const int BiquadStateOffset = 0x0;
|
||||||
|
public const int BiquadStateSize = 0x10;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The state of the biquad filters of this voice.
|
/// The state of the biquad filters of this voice.
|
||||||
|
|
|
@ -16,15 +16,10 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
/// <param name="parameter">The biquad filter parameter</param>
|
/// <param name="parameter">The biquad filter parameter</param>
|
||||||
/// <param name="state">The biquad filter state</param>
|
/// <param name="state">The biquad filter state</param>
|
||||||
/// <param name="outputBuffer">The output buffer to write the result</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="inputBuffer">The input buffer to write the result</param>
|
||||||
/// <param name="sampleCount">The count of samples to process</param>
|
/// <param name="sampleCount">The count of samples to process</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ProcessBiquadFilter(
|
public static void ProcessBiquadFilter(ref BiquadFilterParameter parameter, ref BiquadFilterState state, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
|
||||||
ref BiquadFilterParameter parameter,
|
|
||||||
ref BiquadFilterState state,
|
|
||||||
Span<float> outputBuffer,
|
|
||||||
ReadOnlySpan<float> inputBuffer,
|
|
||||||
uint sampleCount)
|
|
||||||
{
|
{
|
||||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
||||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
||||||
|
@ -45,96 +40,6 @@ 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>
|
/// <summary>
|
||||||
/// Apply multiple biquad filter.
|
/// Apply multiple biquad filter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -142,15 +47,10 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
/// <param name="parameters">The biquad filter parameter</param>
|
/// <param name="parameters">The biquad filter parameter</param>
|
||||||
/// <param name="states">The biquad filter state</param>
|
/// <param name="states">The biquad filter state</param>
|
||||||
/// <param name="outputBuffer">The output buffer to write the result</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="inputBuffer">The input buffer to write the result</param>
|
||||||
/// <param name="sampleCount">The count of samples to process</param>
|
/// <param name="sampleCount">The count of samples to process</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ProcessBiquadFilter(
|
public static void ProcessBiquadFilter(ReadOnlySpan<BiquadFilterParameter> parameters, Span<BiquadFilterState> states, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
|
||||||
ReadOnlySpan<BiquadFilterParameter> parameters,
|
|
||||||
Span<BiquadFilterState> states,
|
|
||||||
Span<float> outputBuffer,
|
|
||||||
ReadOnlySpan<float> inputBuffer,
|
|
||||||
uint sampleCount)
|
|
||||||
{
|
{
|
||||||
for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++)
|
for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++)
|
||||||
{
|
{
|
||||||
|
@ -167,7 +67,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
float input = stageIndex != 0 ? outputBuffer[i] : inputBuffer[i];
|
float input = inputBuffer[i];
|
||||||
float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
|
float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
|
||||||
|
|
||||||
state.State1 = state.State0;
|
state.State1 = state.State0;
|
||||||
|
@ -179,129 +79,5 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
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,10 +30,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
CopyMixBuffer,
|
CopyMixBuffer,
|
||||||
LimiterVersion1,
|
LimiterVersion1,
|
||||||
LimiterVersion2,
|
LimiterVersion2,
|
||||||
MultiTapBiquadFilter,
|
GroupedBiquadFilter,
|
||||||
CaptureBuffer,
|
CaptureBuffer,
|
||||||
Compressor,
|
Compressor,
|
||||||
BiquadFilterAndMix,
|
|
||||||
MultiTapBiquadFilterAndMix,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ using System;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
{
|
{
|
||||||
public class MultiTapBiquadFilterCommand : ICommand
|
public class GroupedBiquadFilterCommand : ICommand
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
public int NodeId { get; }
|
public int NodeId { get; }
|
||||||
|
|
||||||
public CommandType CommandType => CommandType.MultiTapBiquadFilter;
|
public CommandType CommandType => CommandType.GroupedBiquadFilter;
|
||||||
|
|
||||||
public uint EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
private readonly int _outputBufferIndex;
|
private readonly int _outputBufferIndex;
|
||||||
private readonly bool[] _isInitialized;
|
private readonly bool[] _isInitialized;
|
||||||
|
|
||||||
public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
public GroupedBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
||||||
{
|
{
|
||||||
_parameters = filters.ToArray();
|
_parameters = filters.ToArray();
|
||||||
_biquadFilterStates = biquadFilterStateMemory;
|
_biquadFilterStates = biquadFilterStateMemory;
|
|
@ -24,14 +24,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
public Memory<VoiceUpdateState> State { get; }
|
public Memory<VoiceUpdateState> State { get; }
|
||||||
|
|
||||||
public MixRampGroupedCommand(
|
public MixRampGroupedCommand(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> volume0, Span<float> volume1, Memory<VoiceUpdateState> state, int nodeId)
|
||||||
uint mixBufferCount,
|
|
||||||
uint inputBufferIndex,
|
|
||||||
uint outputBufferIndex,
|
|
||||||
ReadOnlySpan<float> volume0,
|
|
||||||
ReadOnlySpan<float> volume1,
|
|
||||||
Memory<VoiceUpdateState> state,
|
|
||||||
int nodeId)
|
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
MixBufferCount = mixBufferCount;
|
MixBufferCount = mixBufferCount;
|
||||||
|
@ -55,12 +48,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static float ProcessMixRampGrouped(
|
private static float ProcessMixRampGrouped(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, float volume0, float volume1, int sampleCount)
|
||||||
Span<float> outputBuffer,
|
|
||||||
ReadOnlySpan<float> inputBuffer,
|
|
||||||
float volume0,
|
|
||||||
float volume1,
|
|
||||||
int sampleCount)
|
|
||||||
{
|
{
|
||||||
float ramp = (volume1 - volume0) / sampleCount;
|
float ramp = (volume1 - volume0) / sampleCount;
|
||||||
float volume = volume0;
|
float volume = volume0;
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,16 +2,12 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x20)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
|
||||||
public struct BiquadFilterState
|
public struct BiquadFilterState
|
||||||
{
|
{
|
||||||
public float State0;
|
public float State0;
|
||||||
public float State1;
|
public float State1;
|
||||||
public float State2;
|
public float State2;
|
||||||
public float State3;
|
public float State3;
|
||||||
public float State4;
|
|
||||||
public float State5;
|
|
||||||
public float State6;
|
|
||||||
public float State7;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
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,4 +1,3 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -6,10 +5,10 @@ using System.Runtime.InteropServices;
|
||||||
namespace Ryujinx.Audio.Renderer.Parameter
|
namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input header for a splitter destination version 1 update.
|
/// Input header for a splitter destination update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct SplitterDestinationInParameterVersion1 : ISplitterDestinationInParameter
|
public struct SplitterDestinationInParameter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Magic of the input header.
|
/// Magic of the input header.
|
||||||
|
@ -42,7 +41,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe fixed byte _reserved[3];
|
private unsafe fixed byte _reserved[3];
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)]
|
||||||
private struct MixArray { }
|
private struct MixArray { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -51,14 +50,6 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||||
public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mixBufferVolume);
|
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>
|
/// <summary>
|
||||||
/// The expected constant of any input header.
|
/// The expected constant of any input header.
|
||||||
/// </summary>
|
/// </summary>
|
|
@ -1,81 +0,0 @@
|
||||||
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,7 +1,6 @@
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Audio.Renderer.Common;
|
using Ryujinx.Audio.Renderer.Common;
|
||||||
using Ryujinx.Audio.Renderer.Dsp.Command;
|
using Ryujinx.Audio.Renderer.Dsp.Command;
|
||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
|
||||||
using Ryujinx.Audio.Renderer.Parameter;
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
using Ryujinx.Audio.Renderer.Server.Effect;
|
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
||||||
|
@ -174,22 +173,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
return ResultCode.WorkBufferTooSmall;
|
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.
|
// Invalidate DSP cache on what was currently allocated with workBuffer.
|
||||||
AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
|
AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
|
||||||
|
|
||||||
|
@ -309,7 +292,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu);
|
state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator, splitterBqfStates))
|
if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator))
|
||||||
{
|
{
|
||||||
return ResultCode.WorkBufferTooSmall;
|
return ResultCode.WorkBufferTooSmall;
|
||||||
}
|
}
|
||||||
|
@ -792,13 +775,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
// Splitter
|
// Splitter
|
||||||
size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter);
|
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
|
// DSP Voice
|
||||||
size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align);
|
size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align);
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// <see cref="Parameter.RendererInfoOutStatus"/> was added to supply the count of update done sent to the DSP.
|
/// <see cref="Parameter.RendererInfoOutStatus"/> was added to supply the count of update done sent to the DSP.
|
||||||
/// A new version of the command estimator was added to address timing changes caused by the voice changes.
|
/// A new version of the command estimator was added to address timing changes caused by the voice changes.
|
||||||
/// Additionally, the rendering limit percent was incremented to 80%.
|
/// Additionally, the rendering limit percent was incremented to 80%.
|
||||||
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This was added in system update 6.0.0</remarks>
|
/// <remarks>This was added in system update 6.0.0</remarks>
|
||||||
public const int Revision5 = 5 << 24;
|
public const int Revision5 = 5 << 24;
|
||||||
|
@ -100,18 +101,10 @@ 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>
|
/// <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;
|
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>
|
/// <summary>
|
||||||
/// Last revision supported by the implementation.
|
/// Last revision supported by the implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int LastRevision = Revision12;
|
public const int LastRevision = Revision11;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Target revision magic supported by the implementation.
|
/// Target revision magic supported by the implementation.
|
||||||
|
@ -361,7 +354,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.
|
/// 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>
|
/// </summary>
|
||||||
/// <returns>True if the audio renderer should use the optimization.</returns>
|
/// <returns>True if the audio renderer should use the optimization.</returns>
|
||||||
public bool UseMultiTapBiquadFilterProcessing()
|
public bool IsBiquadFilterGroupedOptimizationSupported()
|
||||||
{
|
{
|
||||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
|
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
|
||||||
}
|
}
|
||||||
|
@ -375,15 +368,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision11);
|
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>
|
/// <summary>
|
||||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -204,7 +204,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="MultiTapBiquadFilterCommand"/>.
|
/// Create a new <see cref="GroupedBiquadFilterCommand"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseIndex">The base index of the input and output buffer.</param>
|
/// <param name="baseIndex">The base index of the input and output buffer.</param>
|
||||||
/// <param name="filters">The biquad filter parameters.</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="outputBufferOffset">The output buffer offset.</param>
|
||||||
/// <param name="isInitialized">Set to true if the biquad filter state is initialized.</param>
|
/// <param name="isInitialized">Set to true if the biquad filter state is initialized.</param>
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
/// <param name="nodeId">The node id associated to this command.</param>
|
||||||
public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
public void GenerateGroupedBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
|
||||||
{
|
{
|
||||||
MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId);
|
GroupedBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId);
|
||||||
|
|
||||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// <param name="volume">The new volume.</param>
|
/// <param name="volume">The new volume.</param>
|
||||||
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
|
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
/// <param name="nodeId">The node id associated to this command.</param>
|
||||||
public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan<float> previousVolume, ReadOnlySpan<float> volume, Memory<VoiceUpdateState> state, int nodeId)
|
public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> previousVolume, Span<float> volume, Memory<VoiceUpdateState> state, int nodeId)
|
||||||
{
|
{
|
||||||
MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId);
|
MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId);
|
||||||
|
|
||||||
|
@ -260,120 +260,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
AddCommand(command);
|
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>
|
/// <summary>
|
||||||
/// Generate a new <see cref="DepopForMixBuffersCommand"/>.
|
/// Generate a new <see cref="DepopForMixBuffersCommand"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -382,7 +268,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// <param name="bufferCount">The buffer count.</param>
|
/// <param name="bufferCount">The buffer count.</param>
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
/// <param name="nodeId">The node id associated to this command.</param>
|
||||||
/// <param name="sampleRate">The target sample rate in use.</param>
|
/// <param name="sampleRate">The target sample rate in use.</param>
|
||||||
public void GenerateDepopForMixBuffers(Memory<float> depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate)
|
public void GenerateDepopForMixBuffersCommand(Memory<float> depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate)
|
||||||
{
|
{
|
||||||
DepopForMixBuffersCommand command = new(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate);
|
DepopForMixBuffersCommand command = new(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ using Ryujinx.Audio.Renderer.Server.Voice;
|
||||||
using Ryujinx.Audio.Renderer.Utils;
|
using Ryujinx.Audio.Renderer.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Server
|
namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
|
@ -47,13 +46,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
|
ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
|
||||||
|
|
||||||
_commandBuffer.GenerateDepopPrepare(
|
_commandBuffer.GenerateDepopPrepare(dspState,
|
||||||
dspState,
|
_rendererContext.DepopBuffer,
|
||||||
_rendererContext.DepopBuffer,
|
mix.BufferCount,
|
||||||
mix.BufferCount,
|
mix.BufferOffset,
|
||||||
mix.BufferOffset,
|
voiceState.NodeId,
|
||||||
voiceState.NodeId,
|
voiceState.WasPlaying);
|
||||||
voiceState.WasPlaying);
|
|
||||||
}
|
}
|
||||||
else if (voiceState.SplitterId != Constants.UnusedSplitterId)
|
else if (voiceState.SplitterId != Constants.UnusedSplitterId)
|
||||||
{
|
{
|
||||||
|
@ -61,13 +59,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++);
|
Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++);
|
||||||
|
|
||||||
if (destination.IsNull)
|
if (destinationSpan.IsEmpty)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref SplitterDestination destination = ref destinationSpan[0];
|
||||||
|
|
||||||
if (destination.IsConfigured())
|
if (destination.IsConfigured())
|
||||||
{
|
{
|
||||||
int mixId = destination.DestinationId;
|
int mixId = destination.DestinationId;
|
||||||
|
@ -76,13 +76,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
ref MixState mix = ref _mixContext.GetState(mixId);
|
ref MixState mix = ref _mixContext.GetState(mixId);
|
||||||
|
|
||||||
_commandBuffer.GenerateDepopPrepare(
|
_commandBuffer.GenerateDepopPrepare(dspState,
|
||||||
dspState,
|
_rendererContext.DepopBuffer,
|
||||||
_rendererContext.DepopBuffer,
|
mix.BufferCount,
|
||||||
mix.BufferCount,
|
mix.BufferOffset,
|
||||||
mix.BufferOffset,
|
voiceState.NodeId,
|
||||||
voiceState.NodeId,
|
voiceState.WasPlaying);
|
||||||
voiceState.WasPlaying);
|
|
||||||
|
|
||||||
destination.MarkAsNeedToUpdateInternalState();
|
destination.MarkAsNeedToUpdateInternalState();
|
||||||
}
|
}
|
||||||
|
@ -96,39 +95,35 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported())
|
if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported())
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateDataSourceVersion2(
|
_commandBuffer.GenerateDataSourceVersion2(ref voiceState,
|
||||||
ref voiceState,
|
dspState,
|
||||||
dspState,
|
(ushort)_rendererContext.MixBufferCount,
|
||||||
(ushort)_rendererContext.MixBufferCount,
|
(ushort)channelIndex,
|
||||||
(ushort)channelIndex,
|
voiceState.NodeId);
|
||||||
voiceState.NodeId);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (voiceState.SampleFormat)
|
switch (voiceState.SampleFormat)
|
||||||
{
|
{
|
||||||
case SampleFormat.PcmInt16:
|
case SampleFormat.PcmInt16:
|
||||||
_commandBuffer.GeneratePcmInt16DataSourceVersion1(
|
_commandBuffer.GeneratePcmInt16DataSourceVersion1(ref voiceState,
|
||||||
ref voiceState,
|
dspState,
|
||||||
dspState,
|
(ushort)_rendererContext.MixBufferCount,
|
||||||
(ushort)_rendererContext.MixBufferCount,
|
(ushort)channelIndex,
|
||||||
(ushort)channelIndex,
|
voiceState.NodeId);
|
||||||
voiceState.NodeId);
|
|
||||||
break;
|
break;
|
||||||
case SampleFormat.PcmFloat:
|
case SampleFormat.PcmFloat:
|
||||||
_commandBuffer.GeneratePcmFloatDataSourceVersion1(
|
_commandBuffer.GeneratePcmFloatDataSourceVersion1(ref voiceState,
|
||||||
ref voiceState,
|
dspState,
|
||||||
dspState,
|
(ushort)_rendererContext.MixBufferCount,
|
||||||
(ushort)_rendererContext.MixBufferCount,
|
(ushort)channelIndex,
|
||||||
(ushort)channelIndex,
|
voiceState.NodeId);
|
||||||
voiceState.NodeId);
|
|
||||||
break;
|
break;
|
||||||
case SampleFormat.Adpcm:
|
case SampleFormat.Adpcm:
|
||||||
_commandBuffer.GenerateAdpcmDataSourceVersion1(
|
_commandBuffer.GenerateAdpcmDataSourceVersion1(ref voiceState,
|
||||||
ref voiceState,
|
dspState,
|
||||||
dspState,
|
(ushort)_rendererContext.MixBufferCount,
|
||||||
(ushort)_rendererContext.MixBufferCount,
|
voiceState.NodeId);
|
||||||
voiceState.NodeId);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}");
|
throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}");
|
||||||
|
@ -139,14 +134,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
|
private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
|
||||||
{
|
{
|
||||||
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
|
bool supportsOptimizedPath = _rendererContext.BehaviourContext.IsBiquadFilterGroupedOptimizationSupported();
|
||||||
|
|
||||||
if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
|
if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
|
||||||
{
|
{
|
||||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
|
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)];
|
||||||
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
||||||
|
|
||||||
_commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
|
_commandBuffer.GenerateGroupedBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -156,134 +151,33 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
if (filter.Enable)
|
if (filter.Enable)
|
||||||
{
|
{
|
||||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
|
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)];
|
||||||
|
|
||||||
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
||||||
|
|
||||||
_commandBuffer.GenerateBiquadFilter(
|
_commandBuffer.GenerateBiquadFilter(baseIndex,
|
||||||
baseIndex,
|
ref filter,
|
||||||
ref filter,
|
stateMemory.Slice(i, 1),
|
||||||
stateMemory.Slice(i, 1),
|
bufferOffset,
|
||||||
bufferOffset,
|
bufferOffset,
|
||||||
bufferOffset,
|
!voiceState.BiquadFilterNeedInitialization[i],
|
||||||
!voiceState.BiquadFilterNeedInitialization[i],
|
nodeId);
|
||||||
nodeId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateVoiceMixWithSplitter(
|
private void GenerateVoiceMix(Span<float> mixVolumes, Span<float> previousMixVolumes, Memory<VoiceUpdateState> state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId)
|
||||||
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)
|
if (bufferCount > Constants.VoiceChannelCountMax)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateMixRampGrouped(
|
_commandBuffer.GenerateMixRampGrouped(bufferCount,
|
||||||
bufferCount,
|
bufferIndex,
|
||||||
bufferIndex,
|
bufferOffset,
|
||||||
bufferOffset,
|
previousMixVolumes,
|
||||||
previousMixVolumes,
|
mixVolumes,
|
||||||
mixVolumes,
|
state,
|
||||||
state,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -294,14 +188,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
if (mixVolume != 0.0f || previousMixVolume != 0.0f)
|
if (mixVolume != 0.0f || previousMixVolume != 0.0f)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateMixRamp(
|
_commandBuffer.GenerateMixRamp(previousMixVolume,
|
||||||
previousMixVolume,
|
mixVolume,
|
||||||
mixVolume,
|
bufferIndex,
|
||||||
bufferIndex,
|
bufferOffset + (uint)i,
|
||||||
bufferOffset + (uint)i,
|
i,
|
||||||
i,
|
state,
|
||||||
state,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,11 +271,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandBuffer.GenerateVolumeRamp(
|
_commandBuffer.GenerateVolumeRamp(voiceState.PreviousVolume,
|
||||||
voiceState.PreviousVolume,
|
voiceState.Volume,
|
||||||
voiceState.Volume,
|
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
|
|
||||||
if (performanceInitialized)
|
if (performanceInitialized)
|
||||||
{
|
{
|
||||||
|
@ -399,13 +291,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
|
Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
|
||||||
|
|
||||||
if (destination.IsNull)
|
if (destinationSpan.IsEmpty)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref SplitterDestination destination = ref destinationSpan[0];
|
||||||
|
|
||||||
destinationId += (int)channelsCount;
|
destinationId += (int)channelsCount;
|
||||||
|
|
||||||
if (destination.IsConfigured())
|
if (destination.IsConfigured())
|
||||||
|
@ -416,27 +310,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
ref MixState mix = ref _mixContext.GetState(mixId);
|
ref MixState mix = ref _mixContext.GetState(mixId);
|
||||||
|
|
||||||
if (destination.IsBiquadFilterEnabled())
|
GenerateVoiceMix(destination.MixBufferVolume,
|
||||||
{
|
destination.PreviousMixBufferVolume,
|
||||||
GenerateVoiceMixWithSplitter(
|
dspStateMemory,
|
||||||
destination,
|
mix.BufferOffset,
|
||||||
dspStateMemory,
|
mix.BufferCount,
|
||||||
mix.BufferOffset,
|
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||||
mix.BufferCount,
|
nodeId);
|
||||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
|
||||||
nodeId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GenerateVoiceMix(
|
|
||||||
destination.MixBufferVolume,
|
|
||||||
destination.PreviousMixBufferVolume,
|
|
||||||
dspStateMemory,
|
|
||||||
mix.BufferOffset,
|
|
||||||
mix.BufferCount,
|
|
||||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
|
||||||
nodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
destination.MarkAsNeedToUpdateInternalState();
|
destination.MarkAsNeedToUpdateInternalState();
|
||||||
}
|
}
|
||||||
|
@ -457,14 +337,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
GenerateVoiceMix(
|
GenerateVoiceMix(channelResource.Mix.AsSpan(),
|
||||||
channelResource.Mix.AsSpan(),
|
channelResource.PreviousMix.AsSpan(),
|
||||||
channelResource.PreviousMix.AsSpan(),
|
dspStateMemory,
|
||||||
dspStateMemory,
|
mix.BufferOffset,
|
||||||
mix.BufferOffset,
|
mix.BufferCount,
|
||||||
mix.BufferCount,
|
_rendererContext.MixBufferCount + (uint)channelIndex,
|
||||||
_rendererContext.MixBufferCount + (uint)channelIndex,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
|
|
||||||
if (performanceInitialized)
|
if (performanceInitialized)
|
||||||
{
|
{
|
||||||
|
@ -530,11 +409,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
if (effect.Parameter.Volumes[i] != 0.0f)
|
if (effect.Parameter.Volumes[i] != 0.0f)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateMix(
|
_commandBuffer.GenerateMix((uint)bufferOffset + effect.Parameter.Input[i],
|
||||||
(uint)bufferOffset + effect.Parameter.Input[i],
|
(uint)bufferOffset + effect.Parameter.Output[i],
|
||||||
(uint)bufferOffset + effect.Parameter.Output[i],
|
nodeId,
|
||||||
nodeId,
|
effect.Parameter.Volumes[i]);
|
||||||
effect.Parameter.Volumes[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -569,18 +447,17 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
updateCount = newUpdateCount;
|
updateCount = newUpdateCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandBuffer.GenerateAuxEffect(
|
_commandBuffer.GenerateAuxEffect(bufferOffset,
|
||||||
bufferOffset,
|
effect.Parameter.Input[i],
|
||||||
effect.Parameter.Input[i],
|
effect.Parameter.Output[i],
|
||||||
effect.Parameter.Output[i],
|
ref effect.State,
|
||||||
ref effect.State,
|
effect.IsEnabled,
|
||||||
effect.IsEnabled,
|
effect.Parameter.BufferStorageSize,
|
||||||
effect.Parameter.BufferStorageSize,
|
effect.State.SendBufferInfoBase,
|
||||||
effect.State.SendBufferInfoBase,
|
effect.State.ReturnBufferInfoBase,
|
||||||
effect.State.ReturnBufferInfoBase,
|
updateCount,
|
||||||
updateCount,
|
writeOffset,
|
||||||
writeOffset,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
|
|
||||||
writeOffset = newUpdateCount;
|
writeOffset = newUpdateCount;
|
||||||
|
|
||||||
|
@ -623,7 +500,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
if (effect.IsEnabled)
|
if (effect.IsEnabled)
|
||||||
{
|
{
|
||||||
bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
|
bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
|
||||||
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
|
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
|
||||||
|
|
||||||
BiquadFilterParameter parameter = new()
|
BiquadFilterParameter parameter = new()
|
||||||
{
|
{
|
||||||
|
@ -635,14 +512,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
|
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateBiquadFilter(
|
_commandBuffer.GenerateBiquadFilter((int)bufferOffset, ref parameter, effect.State.Slice(i, 1),
|
||||||
(int)bufferOffset,
|
effect.Parameter.Input[i],
|
||||||
ref parameter,
|
effect.Parameter.Output[i],
|
||||||
effect.State.Slice(i, 1),
|
needInitialization,
|
||||||
effect.Parameter.Input[i],
|
nodeId);
|
||||||
effect.Parameter.Output[i],
|
|
||||||
needInitialization,
|
|
||||||
nodeId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -717,16 +591,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
updateCount = newUpdateCount;
|
updateCount = newUpdateCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandBuffer.GenerateCaptureEffect(
|
_commandBuffer.GenerateCaptureEffect(bufferOffset,
|
||||||
bufferOffset,
|
effect.Parameter.Input[i],
|
||||||
effect.Parameter.Input[i],
|
effect.State.SendBufferInfo,
|
||||||
effect.State.SendBufferInfo,
|
effect.IsEnabled,
|
||||||
effect.IsEnabled,
|
effect.Parameter.BufferStorageSize,
|
||||||
effect.Parameter.BufferStorageSize,
|
effect.State.SendBufferInfoBase,
|
||||||
effect.State.SendBufferInfoBase,
|
updateCount,
|
||||||
updateCount,
|
writeOffset,
|
||||||
writeOffset,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
|
|
||||||
writeOffset = newUpdateCount;
|
writeOffset = newUpdateCount;
|
||||||
|
|
||||||
|
@ -739,12 +612,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.Compressor);
|
Debug.Assert(effect.Type == EffectType.Compressor);
|
||||||
|
|
||||||
_commandBuffer.GenerateCompressorEffect(
|
_commandBuffer.GenerateCompressorEffect(bufferOffset,
|
||||||
bufferOffset,
|
effect.Parameter,
|
||||||
effect.Parameter,
|
effect.State,
|
||||||
effect.State,
|
effect.IsEnabled,
|
||||||
effect.IsEnabled,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
|
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
|
||||||
|
@ -757,11 +629,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
bool performanceInitialized = false;
|
bool performanceInitialized = false;
|
||||||
|
|
||||||
if (_performanceManager != null && _performanceManager.GetNextEntry(
|
if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(),
|
||||||
out performanceEntry,
|
isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId))
|
||||||
effect.GetPerformanceDetailType(),
|
|
||||||
isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix,
|
|
||||||
nodeId))
|
|
||||||
{
|
{
|
||||||
performanceInitialized = true;
|
performanceInitialized = true;
|
||||||
|
|
||||||
|
@ -837,85 +706,6 @@ 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)
|
private void GenerateMix(ref MixState mix)
|
||||||
{
|
{
|
||||||
if (mix.HasAnyDestination())
|
if (mix.HasAnyDestination())
|
||||||
|
@ -932,13 +722,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
int destinationIndex = destinationId++;
|
int destinationIndex = destinationId++;
|
||||||
|
|
||||||
SplitterDestination destination = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
|
Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
|
||||||
|
|
||||||
if (destination.IsNull)
|
if (destinationSpan.IsEmpty)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref SplitterDestination destination = ref destinationSpan[0];
|
||||||
|
|
||||||
if (destination.IsConfigured())
|
if (destination.IsConfigured())
|
||||||
{
|
{
|
||||||
int mixId = destination.DestinationId;
|
int mixId = destination.DestinationId;
|
||||||
|
@ -949,32 +741,16 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
|
uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
|
||||||
|
|
||||||
bool isFirstMixBuffer = true;
|
|
||||||
|
|
||||||
for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
|
for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
|
||||||
{
|
{
|
||||||
float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
|
float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
|
||||||
|
|
||||||
if (volume != 0.0f)
|
if (volume != 0.0f)
|
||||||
{
|
{
|
||||||
if (destination.IsBiquadFilterEnabled())
|
_commandBuffer.GenerateMix(inputBufferIndex,
|
||||||
{
|
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||||
GenerateMixWithSplitter(
|
mix.NodeId,
|
||||||
inputBufferIndex,
|
volume);
|
||||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
|
||||||
volume,
|
|
||||||
destination,
|
|
||||||
ref isFirstMixBuffer,
|
|
||||||
mix.NodeId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_commandBuffer.GenerateMix(
|
|
||||||
inputBufferIndex,
|
|
||||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
|
||||||
mix.NodeId,
|
|
||||||
volume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -994,11 +770,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
if (volume != 0.0f)
|
if (volume != 0.0f)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateMix(
|
_commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex,
|
||||||
mix.BufferOffset + bufferIndex,
|
destinationMix.BufferOffset + bufferDestinationIndex,
|
||||||
destinationMix.BufferOffset + bufferDestinationIndex,
|
mix.NodeId,
|
||||||
mix.NodeId,
|
volume);
|
||||||
volume);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1008,12 +783,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
private void GenerateSubMix(ref MixState subMix)
|
private void GenerateSubMix(ref MixState subMix)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateDepopForMixBuffers(
|
_commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
|
||||||
_rendererContext.DepopBuffer,
|
subMix.BufferOffset,
|
||||||
subMix.BufferOffset,
|
subMix.BufferCount,
|
||||||
subMix.BufferCount,
|
subMix.NodeId,
|
||||||
subMix.NodeId,
|
subMix.SampleRate);
|
||||||
subMix.SampleRate);
|
|
||||||
|
|
||||||
GenerateEffects(ref subMix);
|
GenerateEffects(ref subMix);
|
||||||
|
|
||||||
|
@ -1073,12 +847,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
ref MixState finalMix = ref _mixContext.GetFinalState();
|
ref MixState finalMix = ref _mixContext.GetFinalState();
|
||||||
|
|
||||||
_commandBuffer.GenerateDepopForMixBuffers(
|
_commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
|
||||||
_rendererContext.DepopBuffer,
|
finalMix.BufferOffset,
|
||||||
finalMix.BufferOffset,
|
finalMix.BufferCount,
|
||||||
finalMix.BufferCount,
|
finalMix.NodeId,
|
||||||
finalMix.NodeId,
|
finalMix.SampleRate);
|
||||||
finalMix.SampleRate);
|
|
||||||
|
|
||||||
GenerateEffects(ref finalMix);
|
GenerateEffects(ref finalMix);
|
||||||
|
|
||||||
|
@ -1109,10 +882,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandBuffer.GenerateVolume(
|
_commandBuffer.GenerateVolume(finalMix.Volume,
|
||||||
finalMix.Volume,
|
finalMix.BufferOffset + bufferIndex,
|
||||||
finalMix.BufferOffset + bufferIndex,
|
nodeId);
|
||||||
nodeId);
|
|
||||||
|
|
||||||
if (performanceSubInitialized)
|
if (performanceSubInitialized)
|
||||||
{
|
{
|
||||||
|
@ -1166,45 +938,41 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
|
||||||
if (useCustomDownMixingCommand)
|
if (useCustomDownMixingCommand)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateDownMixSurroundToStereo(
|
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
|
||||||
finalMix.BufferOffset,
|
sink.Parameter.Input.AsSpan(),
|
||||||
sink.Parameter.Input.AsSpan(),
|
sink.Parameter.Input.AsSpan(),
|
||||||
sink.Parameter.Input.AsSpan(),
|
sink.DownMixCoefficients,
|
||||||
sink.DownMixCoefficients,
|
Constants.InvalidNodeId);
|
||||||
Constants.InvalidNodeId);
|
|
||||||
}
|
}
|
||||||
// NOTE: We do the downmixing at the DSP level as it's easier that way.
|
// NOTE: We do the downmixing at the DSP level as it's easier that way.
|
||||||
else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
|
else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateDownMixSurroundToStereo(
|
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
|
||||||
finalMix.BufferOffset,
|
sink.Parameter.Input.AsSpan(),
|
||||||
sink.Parameter.Input.AsSpan(),
|
sink.Parameter.Input.AsSpan(),
|
||||||
sink.Parameter.Input.AsSpan(),
|
Constants.DefaultSurroundToStereoCoefficients,
|
||||||
Constants.DefaultSurroundToStereoCoefficients,
|
Constants.InvalidNodeId);
|
||||||
Constants.InvalidNodeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandList commandList = _commandBuffer.CommandList;
|
CommandList commandList = _commandBuffer.CommandList;
|
||||||
|
|
||||||
if (sink.UpsamplerState != null)
|
if (sink.UpsamplerState != null)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateUpsample(
|
_commandBuffer.GenerateUpsample(finalMix.BufferOffset,
|
||||||
finalMix.BufferOffset,
|
sink.UpsamplerState,
|
||||||
sink.UpsamplerState,
|
sink.Parameter.InputCount,
|
||||||
sink.Parameter.InputCount,
|
sink.Parameter.Input.AsSpan(),
|
||||||
sink.Parameter.Input.AsSpan(),
|
commandList.BufferCount,
|
||||||
commandList.BufferCount,
|
commandList.SampleCount,
|
||||||
commandList.SampleCount,
|
commandList.SampleRate,
|
||||||
commandList.SampleRate,
|
Constants.InvalidNodeId);
|
||||||
Constants.InvalidNodeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandBuffer.GenerateDeviceSink(
|
_commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
|
||||||
finalMix.BufferOffset,
|
sink,
|
||||||
sink,
|
_rendererContext.SessionId,
|
||||||
_rendererContext.SessionId,
|
commandList.Buffers,
|
||||||
commandList.Buffers,
|
Constants.InvalidNodeId);
|
||||||
Constants.InvalidNodeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateSink(BaseSink sink, ref MixState finalMix)
|
private void GenerateSink(BaseSink sink, ref MixState finalMix)
|
||||||
|
|
|
@ -170,7 +170,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Estimate(MultiTapBiquadFilterCommand command)
|
public uint Estimate(GroupedBiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -184,15 +184,5 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
return 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Estimate(MultiTapBiquadFilterCommand command)
|
public uint Estimate(GroupedBiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -476,15 +476,5 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
return 0;
|
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(MultiTapBiquadFilterCommand command)
|
public virtual uint Estimate(GroupedBiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -646,15 +646,5 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
return 0;
|
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 CommandProcessingTimeEstimatorVersion4(uint sampleCount, uint bufferCount) : base(sampleCount, bufferCount) { }
|
||||||
|
|
||||||
public override uint Estimate(MultiTapBiquadFilterCommand command)
|
public override uint Estimate(GroupedBiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
||||||
|
|
||||||
|
|
|
@ -210,53 +210,5 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
_ => 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,10 +33,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
uint Estimate(UpsampleCommand command);
|
uint Estimate(UpsampleCommand command);
|
||||||
uint Estimate(LimiterCommandVersion1 command);
|
uint Estimate(LimiterCommandVersion1 command);
|
||||||
uint Estimate(LimiterCommandVersion2 command);
|
uint Estimate(LimiterCommandVersion2 command);
|
||||||
uint Estimate(MultiTapBiquadFilterCommand command);
|
uint Estimate(GroupedBiquadFilterCommand command);
|
||||||
uint Estimate(CaptureBufferCommand command);
|
uint Estimate(CaptureBufferCommand command);
|
||||||
uint Estimate(CompressorCommand command);
|
uint Estimate(CompressorCommand command);
|
||||||
uint Estimate(BiquadFilterAndMixCommand command);
|
|
||||||
uint Estimate(MultiTapBiquadFilterAndMixCommand command);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,11 +225,11 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
|
||||||
|
|
||||||
for (int i = 0; i < splitter.DestinationCount; i++)
|
for (int i = 0; i < splitter.DestinationCount; i++)
|
||||||
{
|
{
|
||||||
SplitterDestination destination = splitter.GetData(i);
|
Span<SplitterDestination> destination = splitter.GetData(i);
|
||||||
|
|
||||||
if (!destination.IsNull)
|
if (!destination.IsEmpty)
|
||||||
{
|
{
|
||||||
int destinationMixId = destination.DestinationId;
|
int destinationMixId = destination[0].DestinationId;
|
||||||
|
|
||||||
if (destinationMixId != UnusedMixId)
|
if (destinationMixId != UnusedMixId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,12 +18,16 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||||
|
|
||||||
if (version == 2)
|
if (version == 2)
|
||||||
{
|
{
|
||||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2,
|
||||||
|
PerformanceEntryVersion2,
|
||||||
|
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == 1)
|
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}");
|
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");
|
||||||
|
|
|
@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||||
{
|
{
|
||||||
performanceEntry = null;
|
performanceEntry = null;
|
||||||
|
|
||||||
if (_entryDetailIndex >= MaxFrameDetailCount)
|
if (_entryDetailIndex > MaxFrameDetailCount)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||||
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
|
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
|
||||||
};
|
};
|
||||||
|
|
||||||
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
|
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex);
|
||||||
|
|
||||||
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];
|
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Ryujinx.Audio.Renderer.Common;
|
using Ryujinx.Audio.Renderer.Common;
|
||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
|
||||||
using Ryujinx.Audio.Renderer.Parameter;
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
using Ryujinx.Audio.Renderer.Utils;
|
using Ryujinx.Audio.Renderer.Utils;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
@ -16,35 +15,15 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SplitterContext
|
public class SplitterContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Amount of biquad filter states per splitter destination.
|
|
||||||
/// </summary>
|
|
||||||
public const int BqfStatesPerDestination = 4;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Storage for <see cref="SplitterState"/>.
|
/// Storage for <see cref="SplitterState"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Memory<SplitterState> _splitters;
|
private Memory<SplitterState> _splitters;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Storage for <see cref="SplitterDestinationVersion1"/>.
|
/// Storage for <see cref="SplitterDestination"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Memory<SplitterDestinationVersion1> _splitterDestinationsV1;
|
private Memory<SplitterDestination> _splitterDestinations;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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>
|
/// <summary>
|
||||||
/// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.
|
/// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.
|
||||||
|
@ -57,17 +36,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// <param name="behaviourContext">The behaviour context.</param>
|
/// <param name="behaviourContext">The behaviour context.</param>
|
||||||
/// <param name="parameter">The audio renderer configuration.</param>
|
/// <param name="parameter">The audio renderer configuration.</param>
|
||||||
/// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param>
|
/// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param>
|
||||||
/// <param name="splitterBqfStates">Memory to store the biquad filtering state for splitters during processing.</param>
|
|
||||||
/// <returns>Return true if the initialization was successful.</returns>
|
/// <returns>Return true if the initialization was successful.</returns>
|
||||||
public bool Initialize(
|
public bool Initialize(ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator)
|
||||||
ref BehaviourContext behaviourContext,
|
|
||||||
ref AudioRendererConfiguration parameter,
|
|
||||||
WorkBufferAllocator workBufferAllocator,
|
|
||||||
Memory<BiquadFilterState> splitterBqfStates)
|
|
||||||
{
|
{
|
||||||
if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0)
|
if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0)
|
||||||
{
|
{
|
||||||
Setup(Memory<SplitterState>.Empty, Memory<SplitterDestinationVersion1>.Empty, Memory<SplitterDestinationVersion2>.Empty, false);
|
Setup(Memory<SplitterState>.Empty, Memory<SplitterDestination>.Empty, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -86,62 +60,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
splitter = new SplitterState(splitterId++);
|
splitter = new SplitterState(splitterId++);
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory<SplitterDestinationVersion1> splitterDestinationsV1 = Memory<SplitterDestinationVersion1>.Empty;
|
Memory<SplitterDestination> splitterDestinations = workBufferAllocator.Allocate<SplitterDestination>(parameter.SplitterDestinationCount,
|
||||||
Memory<SplitterDestinationVersion2> splitterDestinationsV2 = Memory<SplitterDestinationVersion2>.Empty;
|
SplitterDestination.Alignment);
|
||||||
|
|
||||||
if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled())
|
if (splitterDestinations.IsEmpty)
|
||||||
{
|
{
|
||||||
Version = 1;
|
return false;
|
||||||
|
|
||||||
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++);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
int splitterDestinationId = 0;
|
||||||
|
foreach (ref SplitterDestination data in splitterDestinations.Span)
|
||||||
{
|
{
|
||||||
Version = 2;
|
data = new SplitterDestination(splitterDestinationId++);
|
||||||
|
|
||||||
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);
|
SplitterState.InitializeSplitters(splitters.Span);
|
||||||
|
|
||||||
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
Setup(splitters, splitterDestinations, behaviourContext.IsSplitterBugFixed());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -158,15 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
if (behaviourContext.IsSplitterSupported())
|
if (behaviourContext.IsSplitterSupported())
|
||||||
{
|
{
|
||||||
size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment);
|
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())
|
if (behaviourContext.IsSplitterBugFixed())
|
||||||
{
|
{
|
||||||
|
@ -183,18 +110,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// Setup the <see cref="SplitterContext"/> instance.
|
/// Setup the <see cref="SplitterContext"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="splitters">The <see cref="SplitterState"/> storage.</param>
|
/// <param name="splitters">The <see cref="SplitterState"/> storage.</param>
|
||||||
/// <param name="splitterDestinationsV1">The <see cref="SplitterDestinationVersion1"/> storage.</param>
|
/// <param name="splitterDestinations">The <see cref="SplitterDestination"/> 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>
|
/// <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(
|
private void Setup(Memory<SplitterState> splitters, Memory<SplitterDestination> splitterDestinations, bool isBugFixed)
|
||||||
Memory<SplitterState> splitters,
|
|
||||||
Memory<SplitterDestinationVersion1> splitterDestinationsV1,
|
|
||||||
Memory<SplitterDestinationVersion2> splitterDestinationsV2,
|
|
||||||
bool isBugFixed)
|
|
||||||
{
|
{
|
||||||
_splitters = splitters;
|
_splitters = splitters;
|
||||||
_splitterDestinationsV1 = splitterDestinationsV1;
|
_splitterDestinations = splitterDestinations;
|
||||||
_splitterDestinationsV2 = splitterDestinationsV2;
|
|
||||||
IsBugFixed = isBugFixed;
|
IsBugFixed = isBugFixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +141,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length;
|
return _splitterDestinations.Length / _splitters.Length;
|
||||||
|
|
||||||
return length / _splitters.Length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -259,39 +178,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update one splitter destination data from user parameters.
|
/// Update one or multiple <see cref="SplitterDestination"/> 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>
|
/// </summary>
|
||||||
/// <param name="inputHeader">The splitter header.</param>
|
/// <param name="inputHeader">The splitter header.</param>
|
||||||
/// <param name="input">The raw data after the splitter header.</param>
|
/// <param name="input">The raw data after the splitter header.</param>
|
||||||
|
@ -299,23 +186,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
|
for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
|
||||||
{
|
{
|
||||||
if (Version == 1)
|
ref readonly SplitterDestinationInParameter parameter = ref input.GetRefOrRefToCopy<SplitterDestinationInParameter>(out _);
|
||||||
|
|
||||||
|
Debug.Assert(parameter.IsMagicValid());
|
||||||
|
|
||||||
|
if (parameter.IsMagicValid())
|
||||||
{
|
{
|
||||||
if (!UpdateData<SplitterDestinationInParameterVersion1>(ref input))
|
if (parameter.Id >= 0 && parameter.Id < _splitterDestinations.Length)
|
||||||
{
|
{
|
||||||
break;
|
ref SplitterDestination destination = ref GetDestination(parameter.Id);
|
||||||
}
|
|
||||||
}
|
destination.Update(parameter);
|
||||||
else if (Version == 2)
|
|
||||||
{
|
|
||||||
if (!UpdateData<SplitterDestinationInParameterVersion2>(ref input))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Fail($"Invalid splitter context version {Version}.");
|
input.Rewind(Unsafe.SizeOf<SplitterDestinationInParameter>());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +214,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// <returns>Return true if the update was successful.</returns>
|
/// <returns>Return true if the update was successful.</returns>
|
||||||
public bool Update(ref SequenceReader<byte> input)
|
public bool Update(ref SequenceReader<byte> input)
|
||||||
{
|
{
|
||||||
if (!UsingSplitter())
|
if (_splitterDestinations.IsEmpty || _splitters.IsEmpty)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -364,52 +251,45 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a reference to the splitter destination data at the given <paramref name="id"/>.
|
/// Get a reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The index to use.</param>
|
/// <param name="id">The index to use.</param>
|
||||||
/// <returns>A reference to the splitter destination data at the given <paramref name="id"/>.</returns>
|
/// <returns>A reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.</returns>
|
||||||
public SplitterDestination GetDestination(int id)
|
public ref SplitterDestination GetDestination(int id)
|
||||||
{
|
{
|
||||||
if (_splitterDestinationsV2.IsEmpty)
|
return ref SpanIOHelper.GetFromMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length);
|
||||||
{
|
|
||||||
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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)"/>.
|
/// 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)"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The index to use to get the <see cref="SplitterState"/>.</param>
|
/// <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>
|
/// <param name="destinationId">The index of the <see cref="SplitterDestination"/>.</param>
|
||||||
/// <returns>A <see cref="SplitterDestination"/>.</returns>
|
/// <returns>A <see cref="Span{SplitterDestination}"/>.</returns>
|
||||||
public SplitterDestination GetDestination(int id, int destinationId)
|
public Span<SplitterDestination> GetDestination(int id, int destinationId)
|
||||||
{
|
{
|
||||||
ref SplitterState splitter = ref GetState(id);
|
ref SplitterState splitter = ref GetState(id);
|
||||||
|
|
||||||
return splitter.GetData(destinationId);
|
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>
|
/// <summary>
|
||||||
/// Return true if the audio renderer has any splitters.
|
/// Return true if the audio renderer has any splitters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the audio renderer has any splitters.</returns>
|
/// <returns>True if the audio renderer has any splitters.</returns>
|
||||||
public bool UsingSplitter()
|
public bool UsingSplitter()
|
||||||
{
|
{
|
||||||
return !_splitters.IsEmpty && (!_splitterDestinationsV1.IsEmpty || !_splitterDestinationsV2.IsEmpty);
|
return !_splitters.IsEmpty && !_splitterDestinations.IsEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,198 +1,115 @@
|
||||||
using Ryujinx.Audio.Renderer.Parameter;
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Server.Splitter
|
namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Server state for a splitter destination.
|
/// Server state for a splitter destination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ref struct SplitterDestination
|
[StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)]
|
||||||
|
public struct SplitterDestination
|
||||||
{
|
{
|
||||||
private ref SplitterDestinationVersion1 _v1;
|
public const int Alignment = 0x10;
|
||||||
private ref SplitterDestinationVersion2 _v2;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the splitter destination data reference is null.
|
/// The unique id of this <see cref="SplitterDestination"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsNull => Unsafe.IsNullRef(ref _v1) && Unsafe.IsNullRef(ref _v2);
|
public int Id;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The splitter unique id.
|
|
||||||
/// </summary>
|
|
||||||
public int Id
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v1))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _v1.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _v2.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mix to output the result of the splitter.
|
/// The mix to output the result of the splitter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int DestinationId
|
public int DestinationId;
|
||||||
{
|
|
||||||
get
|
/// <summary>
|
||||||
{
|
/// Mix buffer volumes storage.
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
/// </summary>
|
||||||
{
|
private MixArray _mix;
|
||||||
if (Unsafe.IsNullRef(ref _v1))
|
private MixArray _previousMix;
|
||||||
{
|
|
||||||
return 0;
|
/// <summary>
|
||||||
}
|
/// Pointer to the next linked element.
|
||||||
else
|
/// </summary>
|
||||||
{
|
private unsafe SplitterDestination* _next;
|
||||||
return _v1.DestinationId;
|
|
||||||
}
|
/// <summary>
|
||||||
}
|
/// Set to true if in use.
|
||||||
else
|
/// </summary>
|
||||||
{
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
return _v2.DestinationId;
|
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>
|
/// <summary>
|
||||||
/// Mix buffer volumes.
|
/// Mix buffer volumes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||||
public Span<float> MixBufferVolume
|
public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mix);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v1))
|
|
||||||
{
|
|
||||||
return Span<float>.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _v1.MixBufferVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _v2.MixBufferVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Previous mix buffer volumes.
|
/// Previous mix buffer volumes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
|
||||||
public Span<float> PreviousMixBufferVolume
|
public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v1))
|
|
||||||
{
|
|
||||||
return Span<float>.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _v1.PreviousMixBufferVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return _v2.PreviousMixBufferVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the <see cref="SplitterDestination"/> of the next element or null if not present.
|
/// Get the <see cref="Span{SplitterDestination}"/> of the next element or <see cref="Span{SplitterDestination}.Empty"/> if not present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly SplitterDestination Next
|
public readonly Span<SplitterDestination> Next
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
return _next != null ? new Span<SplitterDestination>(_next, 1) : Span<SplitterDestination>.Empty;
|
||||||
{
|
|
||||||
if (Unsafe.IsNullRef(ref _v1))
|
|
||||||
{
|
|
||||||
return new SplitterDestination();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new SplitterDestination(ref _v1.Next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new SplitterDestination(ref _v2.Next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new splitter destination wrapper for the version 1 splitter destination data.
|
/// Create a new <see cref="SplitterDestination"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="v1">Version 1 splitter destination data</param>
|
/// <param name="id">The unique id of this <see cref="SplitterDestination"/>.</param>
|
||||||
public SplitterDestination(ref SplitterDestinationVersion1 v1)
|
public SplitterDestination(int id) : this()
|
||||||
{
|
{
|
||||||
_v1 = ref v1;
|
Id = id;
|
||||||
_v2 = ref Unsafe.NullRef<SplitterDestinationVersion2>();
|
DestinationId = Constants.UnusedMixId;
|
||||||
|
|
||||||
|
ClearVolumes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new splitter destination wrapper for the version 2 splitter destination data.
|
/// Update the <see cref="SplitterDestination"/> from user parameter.
|
||||||
/// </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>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
public void Update(SplitterDestinationInParameter parameter)
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
Debug.Assert(Id == parameter.Id);
|
||||||
|
|
||||||
|
if (parameter.IsMagicValid() && Id == parameter.Id)
|
||||||
{
|
{
|
||||||
_v1.Update(parameter);
|
DestinationId = parameter.DestinationId;
|
||||||
}
|
|
||||||
else
|
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||||
{
|
|
||||||
_v2.Update(parameter);
|
if (!IsUsed && parameter.IsUsed)
|
||||||
|
{
|
||||||
|
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||||
|
|
||||||
|
NeedToUpdateInternalState = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsUsed = parameter.IsUsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,14 +118,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateInternalState()
|
public void UpdateInternalState()
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
if (IsUsed && NeedToUpdateInternalState)
|
||||||
{
|
{
|
||||||
_v1.UpdateInternalState();
|
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_v2.UpdateInternalState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NeedToUpdateInternalState = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -216,23 +131,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void MarkAsNeedToUpdateInternalState()
|
public void MarkAsNeedToUpdateInternalState()
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
NeedToUpdateInternalState = true;
|
||||||
{
|
|
||||||
_v1.MarkAsNeedToUpdateInternalState();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_v2.MarkAsNeedToUpdateInternalState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return true if the splitter destination is used and has a destination.
|
/// Return true if the <see cref="SplitterDestination"/> is used and has a destination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the splitter destination is used and has a destination.</returns>
|
/// <returns>True if the <see cref="SplitterDestination"/> is used and has a destination.</returns>
|
||||||
public readonly bool IsConfigured()
|
public readonly bool IsConfigured()
|
||||||
{
|
{
|
||||||
return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured();
|
return IsUsed && DestinationId != Constants.UnusedMixId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -242,17 +150,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// <returns>The volume for the given destination.</returns>
|
/// <returns>The volume for the given destination.</returns>
|
||||||
public float GetMixVolume(int destinationIndex)
|
public float GetMixVolume(int destinationIndex)
|
||||||
{
|
{
|
||||||
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex);
|
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
return MixBufferVolume[destinationIndex];
|
||||||
/// 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>
|
/// <summary>
|
||||||
|
@ -260,33 +160,22 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ClearVolumes()
|
public void ClearVolumes()
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
MixBufferVolume.Clear();
|
||||||
{
|
PreviousMixBufferVolume.Clear();
|
||||||
_v1.ClearVolumes();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_v2.ClearVolumes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Link the next element to the given splitter destination.
|
/// Link the next element to the given <see cref="SplitterDestination"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="next">The given splitter destination to link.</param>
|
/// <param name="next">The given <see cref="SplitterDestination"/> to link.</param>
|
||||||
public void Link(SplitterDestination next)
|
public void Link(ref SplitterDestination next)
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
unsafe
|
||||||
{
|
{
|
||||||
Debug.Assert(!Unsafe.IsNullRef(ref next._v1));
|
fixed (SplitterDestination* nextPtr = &next)
|
||||||
|
{
|
||||||
_v1.Link(ref next._v1);
|
_next = nextPtr;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Assert(!Unsafe.IsNullRef(ref next._v2));
|
|
||||||
|
|
||||||
_v2.Link(ref next._v2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,74 +184,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Unlink()
|
public void Unlink()
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
unsafe
|
||||||
{
|
{
|
||||||
_v1.Unlink();
|
_next = null;
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,206 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,250 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,8 +15,6 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
public const int Alignment = 0x10;
|
public const int Alignment = 0x10;
|
||||||
|
|
||||||
private delegate void SplitterDestinationAction(SplitterDestination destination, int index);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The unique id of this <see cref="SplitterState"/>.
|
/// The unique id of this <see cref="SplitterState"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -28,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
public uint SampleRate;
|
public uint SampleRate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Count of splitter destinations.
|
/// Count of splitter destinations (<see cref="SplitterDestination"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int DestinationCount;
|
public int DestinationCount;
|
||||||
|
|
||||||
|
@ -39,25 +37,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
public bool HasNewConnection;
|
public bool HasNewConnection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Linked list of <see cref="SplitterDestinationVersion1"/>.
|
/// Linked list of <see cref="SplitterDestination"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe SplitterDestinationVersion1* _destinationDataV1;
|
private unsafe SplitterDestination* _destinationsData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Linked list of <see cref="SplitterDestinationVersion2"/>.
|
/// Span to the first element of the linked list of <see cref="SplitterDestination"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe SplitterDestinationVersion2* _destinationDataV2;
|
public readonly Span<SplitterDestination> Destinations
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// First element of the linked list of splitter destinations data.
|
|
||||||
/// </summary>
|
|
||||||
public readonly SplitterDestination Destination
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
return new SplitterDestination(_destinationDataV1, _destinationDataV2);
|
return (IntPtr)_destinationsData != IntPtr.Zero ? new Span<SplitterDestination>(_destinationsData, 1) : Span<SplitterDestination>.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,20 +64,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
Id = id;
|
Id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly SplitterDestination GetData(int index)
|
public readonly Span<SplitterDestination> GetData(int index)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
SplitterDestination result = Destination;
|
Span<SplitterDestination> result = Destinations;
|
||||||
|
|
||||||
while (i < index)
|
while (i < index)
|
||||||
{
|
{
|
||||||
if (result.IsNull)
|
if (result.IsEmpty)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = result.Next;
|
result = result[0].Next;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,25 +93,25 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Utility function to apply an action to all <see cref="Destination"/>.
|
/// Utility function to apply a given <see cref="SpanAction{T, TArg}"/> to all <see cref="Destinations"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="action">The action to execute on each elements.</param>
|
/// <param name="action">The action to execute on each elements.</param>
|
||||||
private readonly void ForEachDestination(SplitterDestinationAction action)
|
private readonly void ForEachDestination(SpanAction<SplitterDestination, int> action)
|
||||||
{
|
{
|
||||||
SplitterDestination temp = Destination;
|
Span<SplitterDestination> temp = Destinations;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (temp.IsNull)
|
if (temp.IsEmpty)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitterDestination next = temp.Next;
|
Span<SplitterDestination> next = temp[0].Next;
|
||||||
|
|
||||||
action(temp, i++);
|
action.Invoke(temp, i++);
|
||||||
|
|
||||||
temp = next;
|
temp = next;
|
||||||
}
|
}
|
||||||
|
@ -149,9 +142,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
input.ReadLittleEndian(out int destinationId);
|
input.ReadLittleEndian(out int destinationId);
|
||||||
|
|
||||||
SplitterDestination destination = context.GetDestination(destinationId);
|
Memory<SplitterDestination> destination = context.GetDestinationMemory(destinationId);
|
||||||
|
|
||||||
SetDestination(destination);
|
SetDestination(ref destination.Span[0]);
|
||||||
|
|
||||||
DestinationCount = destinationCount;
|
DestinationCount = destinationCount;
|
||||||
|
|
||||||
|
@ -159,9 +152,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
input.ReadLittleEndian(out destinationId);
|
input.ReadLittleEndian(out destinationId);
|
||||||
|
|
||||||
SplitterDestination nextDestination = context.GetDestination(destinationId);
|
Memory<SplitterDestination> nextDestination = context.GetDestinationMemory(destinationId);
|
||||||
|
|
||||||
destination.Link(nextDestination);
|
destination.Span[0].Link(ref nextDestination.Span[0]);
|
||||||
destination = nextDestination;
|
destination = nextDestination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,21 +174,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the head of the linked list of <see cref="Destination"/>.
|
/// Set the head of the linked list of <see cref="Destinations"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newValue">New destination value.</param>
|
/// <param name="newValue">A reference to a <see cref="SplitterDestination"/>.</param>
|
||||||
public void SetDestination(SplitterDestination newValue)
|
public void SetDestination(ref SplitterDestination newValue)
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (SplitterDestinationVersion1* newValuePtr = &newValue.GetV1RefOrNull())
|
fixed (SplitterDestination* newValuePtr = &newValue)
|
||||||
{
|
{
|
||||||
_destinationDataV1 = newValuePtr;
|
_destinationsData = newValuePtr;
|
||||||
}
|
|
||||||
|
|
||||||
fixed (SplitterDestinationVersion2* newValuePtr = &newValue.GetV2RefOrNull())
|
|
||||||
{
|
|
||||||
_destinationDataV2 = newValuePtr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,20 +193,19 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly void UpdateInternalState()
|
public readonly void UpdateInternalState()
|
||||||
{
|
{
|
||||||
ForEachDestination((destination, _) => destination.UpdateInternalState());
|
ForEachDestination((destination, _) => destination[0].UpdateInternalState());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clear all links from the <see cref="Destination"/>.
|
/// Clear all links from the <see cref="Destinations"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ClearLinks()
|
public void ClearLinks()
|
||||||
{
|
{
|
||||||
ForEachDestination((destination, _) => destination.Unlink());
|
ForEachDestination((destination, _) => destination[0].Unlink());
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
_destinationDataV1 = null;
|
_destinationsData = (SplitterDestination*)IntPtr.Zero;
|
||||||
_destinationDataV2 = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +219,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
splitter._destinationDataV1 = null;
|
splitter._destinationsData = (SplitterDestination*)IntPtr.Zero;
|
||||||
splitter._destinationDataV2 = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
splitter.DestinationCount = 0;
|
splitter.DestinationCount = 0;
|
||||||
|
|
|
@ -1,33 +1,13 @@
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Common.GraphicsDriver
|
namespace Ryujinx.Common.GraphicsDriver
|
||||||
{
|
{
|
||||||
public static class DriverUtilities
|
public static class DriverUtilities
|
||||||
{
|
{
|
||||||
private static void AddMesaFlags(string envVar, string newFlags)
|
|
||||||
{
|
|
||||||
string existingFlags = Environment.GetEnvironmentVariable(envVar);
|
|
||||||
|
|
||||||
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
|
|
||||||
|
|
||||||
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InitDriverConfig(bool oglThreading)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
AddMesaFlags("RADV_DEBUG", "nodcc");
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleOGLThreading(oglThreading);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ToggleOGLThreading(bool enabled)
|
public static void ToggleOGLThreading(bool enabled)
|
||||||
{
|
{
|
||||||
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
|
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
|
||||||
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public partial class ByteMemoryPool
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
|
||||||
|
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
|
||||||
|
/// with a length of the requested size.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
|
||||||
|
{
|
||||||
|
private byte[] _array;
|
||||||
|
private readonly int _length;
|
||||||
|
|
||||||
|
public ByteMemoryPoolBuffer(int length)
|
||||||
|
{
|
||||||
|
_array = ArrayPool<byte>.Shared.Rent(length);
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
|
||||||
|
/// </summary>
|
||||||
|
public Memory<byte> Memory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
byte[] array = _array;
|
||||||
|
|
||||||
|
ObjectDisposedException.ThrowIf(array is null, this);
|
||||||
|
|
||||||
|
return new Memory<byte>(array, 0, _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var array = Interlocked.Exchange(ref _array, null);
|
||||||
|
|
||||||
|
if (array != null)
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
src/Ryujinx.Common/Memory/ByteMemoryPool.cs
Normal file
106
src/Ryujinx.Common/Memory/ByteMemoryPool.cs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a pool of re-usable byte array instances.
|
||||||
|
/// </summary>
|
||||||
|
public static partial class ByteMemoryPool
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the maximum buffer size supported by this pool.
|
||||||
|
/// </summary>
|
||||||
|
public static int MaxBufferSize => Array.MaxLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public static IMemoryOwner<byte> Rent(long length)
|
||||||
|
=> RentImpl(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public static IMemoryOwner<byte> Rent(ulong length)
|
||||||
|
=> RentImpl(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public static IMemoryOwner<byte> Rent(int length)
|
||||||
|
=> RentImpl(length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public static IMemoryOwner<byte> RentCleared(long length)
|
||||||
|
=> RentCleared(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public static IMemoryOwner<byte> RentCleared(ulong length)
|
||||||
|
=> RentCleared(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public static IMemoryOwner<byte> RentCleared(int length)
|
||||||
|
{
|
||||||
|
var buffer = RentImpl(length);
|
||||||
|
|
||||||
|
buffer.Memory.Span.Clear();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies <paramref name="buffer"/> into a newly rented byte memory buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The byte buffer to copy</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory with <paramref name="buffer"/> copied to it</returns>
|
||||||
|
public static IMemoryOwner<byte> RentCopy(ReadOnlySpan<byte> buffer)
|
||||||
|
{
|
||||||
|
var copy = RentImpl(buffer.Length);
|
||||||
|
|
||||||
|
buffer.CopyTo(copy.Memory.Span);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteMemoryPoolBuffer RentImpl(int length)
|
||||||
|
{
|
||||||
|
if ((uint)length > Array.MaxLength)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(length), length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ByteMemoryPoolBuffer(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,140 +0,0 @@
|
||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Memory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and fast <see cref="Span{T}"/>
|
|
||||||
/// accessor, with memory allocated from <seealso cref="ArrayPool{T}.Shared"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of item to store.</typeparam>
|
|
||||||
public sealed class MemoryOwner<T> : IMemoryOwner<T>
|
|
||||||
{
|
|
||||||
private readonly int _length;
|
|
||||||
private T[]? _array;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="MemoryOwner{T}"/> class with the specified parameters.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the new memory buffer to use</param>
|
|
||||||
private MemoryOwner(int length)
|
|
||||||
{
|
|
||||||
_length = length;
|
|
||||||
_array = ArrayPool<T>.Shared.Rent(length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the new memory buffer to use</param>
|
|
||||||
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static MemoryOwner<T> Rent(int length) => new(length);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length and the content cleared.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the new memory buffer to use</param>
|
|
||||||
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length and the content cleared</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static MemoryOwner<T> RentCleared(int length)
|
|
||||||
{
|
|
||||||
MemoryOwner<T> result = new(length);
|
|
||||||
|
|
||||||
result._array.AsSpan(0, length).Clear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the content copied from the specified buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The buffer to copy</param>
|
|
||||||
/// <returns>A <see cref="MemoryOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
|
|
||||||
public static MemoryOwner<T> RentCopy(ReadOnlySpan<T> buffer)
|
|
||||||
{
|
|
||||||
MemoryOwner<T> result = new(buffer.Length);
|
|
||||||
|
|
||||||
buffer.CopyTo(result._array);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of items in the current instance.
|
|
||||||
/// </summary>
|
|
||||||
public int Length
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => _length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public Memory<T> Memory
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
T[]? array = _array;
|
|
||||||
|
|
||||||
if (array is null)
|
|
||||||
{
|
|
||||||
ThrowObjectDisposedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new(array, 0, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Uses a trick made possible by the .NET 6+ runtime array layout.
|
|
||||||
/// </remarks>
|
|
||||||
public Span<T> Span
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
T[]? array = _array;
|
|
||||||
|
|
||||||
if (array is null)
|
|
||||||
{
|
|
||||||
ThrowObjectDisposedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(array);
|
|
||||||
|
|
||||||
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
T[]? array = Interlocked.Exchange(ref _array, null);
|
|
||||||
|
|
||||||
if (array is not null)
|
|
||||||
{
|
|
||||||
ArrayPool<T>.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Throws an <see cref="ObjectDisposedException"/> when <see cref="_array"/> is <see langword="null"/>.
|
|
||||||
/// </summary>
|
|
||||||
[DoesNotReturn]
|
|
||||||
private static void ThrowObjectDisposedException()
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(nameof(MemoryOwner<T>), "The buffer has already been disposed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Memory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A stack-only type that rents a buffer of a specified length from <seealso cref="ArrayPool{T}.Shared"/>.
|
|
||||||
/// It does not implement <see cref="IDisposable"/> to avoid being boxed, but should still be disposed. This
|
|
||||||
/// is easy since C# 8, which allows use of C# `using` constructs on any type that has a public Dispose() method.
|
|
||||||
/// To keep this type simple, fast, and read-only, it does not check or guard against multiple disposals.
|
|
||||||
/// For all these reasons, all usage should be with a `using` block or statement.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of item to store.</typeparam>
|
|
||||||
public readonly ref struct SpanOwner<T>
|
|
||||||
{
|
|
||||||
private readonly int _length;
|
|
||||||
private readonly T[] _array;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="SpanOwner{T}"/> struct with the specified parameters.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the new memory buffer to use</param>
|
|
||||||
private SpanOwner(int length)
|
|
||||||
{
|
|
||||||
_length = length;
|
|
||||||
_array = ArrayPool<T>.Shared.Rent(length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an empty <see cref="SpanOwner{T}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public static SpanOwner<T> Empty
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => new(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="SpanOwner{T}"/> instance with the specified length.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the new memory buffer to use</param>
|
|
||||||
/// <returns>A <see cref="SpanOwner{T}"/> instance of the requested length</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static SpanOwner<T> Rent(int length) => new(length);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="SpanOwner{T}"/> instance with the length and the content cleared.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the new memory buffer to use</param>
|
|
||||||
/// <returns>A <see cref="SpanOwner{T}"/> instance of the requested length and the content cleared</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static SpanOwner<T> RentCleared(int length)
|
|
||||||
{
|
|
||||||
SpanOwner<T> result = new(length);
|
|
||||||
|
|
||||||
result._array.AsSpan(0, length).Clear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="SpanOwner{T}"/> instance with the content copied from the specified buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The buffer to copy</param>
|
|
||||||
/// <returns>A <see cref="SpanOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
|
|
||||||
public static SpanOwner<T> RentCopy(ReadOnlySpan<T> buffer)
|
|
||||||
{
|
|
||||||
SpanOwner<T> result = new(buffer.Length);
|
|
||||||
|
|
||||||
buffer.CopyTo(result._array);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of items in the current instance
|
|
||||||
/// </summary>
|
|
||||||
public int Length
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => _length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Uses a trick made possible by the .NET 6+ runtime array layout.
|
|
||||||
/// </remarks>
|
|
||||||
public Span<T> Span
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(_array);
|
|
||||||
|
|
||||||
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implements the duck-typed <see cref="IDisposable.Dispose"/> method.
|
|
||||||
/// </summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
ArrayPool<T>.Shared.Return(_array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
@ -42,14 +42,14 @@ namespace Ryujinx.Common
|
||||||
return StreamUtils.StreamToBytes(stream);
|
return StreamUtils.StreamToBytes(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return ReadFileToRentedMemory(assembly, path);
|
return ReadFileToRentedMemory(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
public static IMemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using var stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
|
||||||
{
|
|
||||||
public partial class OsUtils
|
|
||||||
{
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
|
|
||||||
|
|
||||||
public static void SetEnvironmentVariableNoCaching(string key, string value)
|
|
||||||
{
|
|
||||||
// Set the value in the cached environment variables, too.
|
|
||||||
Environment.SetEnvironmentVariable(key, value);
|
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
int res = setenv(key, value, 1);
|
|
||||||
Debug.Assert(res != -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Microsoft.IO;
|
using Microsoft.IO;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -15,7 +16,7 @@ namespace Ryujinx.Common.Utilities
|
||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
|
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
|
||||||
{
|
{
|
||||||
if (input is MemoryStream inputMemoryStream)
|
if (input is MemoryStream inputMemoryStream)
|
||||||
{
|
{
|
||||||
|
@ -25,9 +26,9 @@ namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
long bytesExpected = input.Length;
|
long bytesExpected = input.Length;
|
||||||
|
|
||||||
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
|
||||||
|
|
||||||
var destSpan = ownedMemory.Span;
|
var destSpan = ownedMemory.Memory.Span;
|
||||||
|
|
||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
|
|
||||||
|
@ -65,14 +66,14 @@ namespace Ryujinx.Common.Utilities
|
||||||
return stream.ToArray();
|
return stream.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
||||||
{
|
{
|
||||||
input.Position = 0;
|
input.Position = 0;
|
||||||
|
|
||||||
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)input.Length));
|
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
|
||||||
|
|
||||||
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
|
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
|
||||||
_ = input.Read(ownedMemory.Span);
|
_ = input.Read(ownedMemory.Memory.Span);
|
||||||
|
|
||||||
return ownedMemory;
|
return ownedMemory;
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
||||||
|
|
||||||
Read(va, memoryOwner.Span);
|
Read(va, memoryOwner.Memory.Span);
|
||||||
|
|
||||||
return new WritableRegion(this, va, memoryOwner);
|
return new WritableRegion(this, va, memoryOwner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
{
|
{
|
||||||
|
|
|
@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Add(d, n, m);
|
context.Arm64Assembler.Add(d, n, m);
|
||||||
EmitSaturateUqadd(context, d, 16);
|
EmitSaturateUnsignedRange(context, d, 16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Add(d, n, m);
|
context.Arm64Assembler.Add(d, n, m);
|
||||||
EmitSaturateUqadd(context, d, 8);
|
EmitSaturateUnsignedRange(context, d, 8);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
context.Arm64Assembler.Add(d, n, m);
|
context.Arm64Assembler.Add(d, n, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitSaturateUq(context, d, 16, e == 0);
|
EmitSaturateUnsignedRange(context, d, 16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,25 +157,25 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
context.Arm64Assembler.Sub(d, n, m);
|
context.Arm64Assembler.Sub(d, n, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitSaturateUq(context, d, 16, e != 0);
|
EmitSaturateUnsignedRange(context, d, 16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
|
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||||
{
|
{
|
||||||
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Sub(d, n, m);
|
context.Arm64Assembler.Sub(d, n, m);
|
||||||
EmitSaturateUqsub(context, d, 16);
|
EmitSaturateUnsignedRange(context, d, 16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
|
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||||
{
|
{
|
||||||
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Sub(d, n, m);
|
context.Arm64Assembler.Sub(d, n, m);
|
||||||
EmitSaturateUqsub(context, d, 8);
|
EmitSaturateUnsignedRange(context, d, 8);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,17 +358,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitSaturateUqadd(CodeGenContext context, Operand value, uint saturateTo)
|
private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo)
|
||||||
{
|
|
||||||
EmitSaturateUq(context, value, saturateTo, isSub: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSaturateUqsub(CodeGenContext context, Operand value, uint saturateTo)
|
|
||||||
{
|
|
||||||
EmitSaturateUq(context, value, saturateTo, isSub: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSaturateUq(CodeGenContext context, Operand value, uint saturateTo, bool isSub)
|
|
||||||
{
|
{
|
||||||
Debug.Assert(saturateTo <= 32);
|
Debug.Assert(saturateTo <= 32);
|
||||||
|
|
||||||
|
@ -389,7 +379,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const((int)saturateTo));
|
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo));
|
||||||
|
|
||||||
int branchIndex = context.CodeWriter.InstructionPointer;
|
int branchIndex = context.CodeWriter.InstructionPointer;
|
||||||
|
|
||||||
|
@ -397,7 +387,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
|
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
|
||||||
|
|
||||||
// Saturate.
|
// Saturate.
|
||||||
context.Arm64Assembler.Mov(value, isSub ? 0u : uint.MaxValue >> (32 - (int)saturateTo));
|
context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo));
|
||||||
|
|
||||||
int delta = context.CodeWriter.InstructionPointer - branchIndex;
|
int delta = context.CodeWriter.InstructionPointer - branchIndex;
|
||||||
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
|
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
@ -144,9 +145,9 @@ namespace Ryujinx.Graphics.Device
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
||||||
|
|
||||||
ReadImpl(va, memoryOwner.Span);
|
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
|
||||||
|
|
||||||
return new WritableRegion(this, va, memoryOwner, tracked: true);
|
return new WritableRegion(this, va, memoryOwner, tracked: true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,7 @@ namespace Ryujinx.Graphics.Device
|
||||||
{
|
{
|
||||||
var field = fields[fieldIndex];
|
var field = fields[fieldIndex];
|
||||||
|
|
||||||
var currentFieldOffset = (int)Marshal.OffsetOf<TState>(field.Name);
|
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
|
||||||
var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf<TState>() : (int)Marshal.OffsetOf<TState>(fields[fieldIndex + 1].Name);
|
|
||||||
|
|
||||||
int sizeOfField = nextFieldOffset - currentFieldOffset;
|
|
||||||
|
|
||||||
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
|
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
|
||||||
{
|
{
|
||||||
|
|
63
src/Ryujinx.Graphics.Device/SizeCalculator.cs
Normal file
63
src/Ryujinx.Graphics.Device/SizeCalculator.cs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Device
|
||||||
|
{
|
||||||
|
public static class SizeCalculator
|
||||||
|
{
|
||||||
|
public static int SizeOf(Type type)
|
||||||
|
{
|
||||||
|
// Is type a enum type?
|
||||||
|
if (type.IsEnum)
|
||||||
|
{
|
||||||
|
type = type.GetEnumUnderlyingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is type a pointer type?
|
||||||
|
if (type.IsPointer || type == typeof(IntPtr) || type == typeof(UIntPtr))
|
||||||
|
{
|
||||||
|
return IntPtr.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is type a struct type?
|
||||||
|
if (type.IsValueType && !type.IsPrimitive)
|
||||||
|
{
|
||||||
|
// Check if the struct has a explicit size, if so, return that.
|
||||||
|
if (type.StructLayoutAttribute.Size != 0)
|
||||||
|
{
|
||||||
|
return type.StructLayoutAttribute.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we calculate the sum of the sizes of all fields.
|
||||||
|
int size = 0;
|
||||||
|
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
|
||||||
|
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
|
||||||
|
{
|
||||||
|
size += SizeOf(fields[fieldIndex].FieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primitive types.
|
||||||
|
return (Type.GetTypeCode(type)) switch
|
||||||
|
{
|
||||||
|
TypeCode.SByte => sizeof(sbyte),
|
||||||
|
TypeCode.Byte => sizeof(byte),
|
||||||
|
TypeCode.Int16 => sizeof(short),
|
||||||
|
TypeCode.UInt16 => sizeof(ushort),
|
||||||
|
TypeCode.Int32 => sizeof(int),
|
||||||
|
TypeCode.UInt32 => sizeof(uint),
|
||||||
|
TypeCode.Int64 => sizeof(long),
|
||||||
|
TypeCode.UInt64 => sizeof(ulong),
|
||||||
|
TypeCode.Char => sizeof(char),
|
||||||
|
TypeCode.Single => sizeof(float),
|
||||||
|
TypeCode.Double => sizeof(double),
|
||||||
|
TypeCode.Decimal => sizeof(decimal),
|
||||||
|
TypeCode.Boolean => sizeof(bool),
|
||||||
|
_ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,8 @@ namespace Ryujinx.Graphics.GAL
|
||||||
public enum BufferAccess
|
public enum BufferAccess
|
||||||
{
|
{
|
||||||
Default = 0,
|
Default = 0,
|
||||||
HostMemory = 1,
|
FlushPersistent = 1 << 0,
|
||||||
DeviceMemory = 2,
|
Stream = 1 << 1,
|
||||||
DeviceMemoryMapped = 3,
|
SparseCompatible = 1 << 2,
|
||||||
|
|
||||||
MemoryTypeMask = 0xf,
|
|
||||||
|
|
||||||
Stream = 1 << 4,
|
|
||||||
SparseCompatible = 1 << 5,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public readonly TargetApi Api;
|
public readonly TargetApi Api;
|
||||||
public readonly string VendorName;
|
public readonly string VendorName;
|
||||||
public readonly SystemMemoryType MemoryType;
|
|
||||||
|
|
||||||
public readonly bool HasFrontFacingBug;
|
public readonly bool HasFrontFacingBug;
|
||||||
public readonly bool HasVectorIndexingBug;
|
public readonly bool HasVectorIndexingBug;
|
||||||
|
@ -37,7 +36,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
public readonly bool SupportsMismatchingViewFormat;
|
public readonly bool SupportsMismatchingViewFormat;
|
||||||
public readonly bool SupportsCubemapView;
|
public readonly bool SupportsCubemapView;
|
||||||
public readonly bool SupportsNonConstantTextureOffset;
|
public readonly bool SupportsNonConstantTextureOffset;
|
||||||
public readonly bool SupportsQuads;
|
|
||||||
public readonly bool SupportsSeparateSampler;
|
public readonly bool SupportsSeparateSampler;
|
||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
public readonly bool SupportsShaderBarrierDivergence;
|
public readonly bool SupportsShaderBarrierDivergence;
|
||||||
|
@ -51,13 +49,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
public readonly bool SupportsIndirectParameters;
|
public readonly bool SupportsIndirectParameters;
|
||||||
public readonly bool SupportsDepthClipControl;
|
public readonly bool SupportsDepthClipControl;
|
||||||
|
|
||||||
public readonly int UniformBufferSetIndex;
|
|
||||||
public readonly int StorageBufferSetIndex;
|
|
||||||
public readonly int TextureSetIndex;
|
|
||||||
public readonly int ImageSetIndex;
|
|
||||||
public readonly int ExtraSetBaseIndex;
|
|
||||||
public readonly int MaximumExtraSets;
|
|
||||||
|
|
||||||
public readonly uint MaximumUniformBuffersPerStage;
|
public readonly uint MaximumUniformBuffersPerStage;
|
||||||
public readonly uint MaximumStorageBuffersPerStage;
|
public readonly uint MaximumStorageBuffersPerStage;
|
||||||
public readonly uint MaximumTexturesPerStage;
|
public readonly uint MaximumTexturesPerStage;
|
||||||
|
@ -71,12 +62,9 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
public readonly int GatherBiasPrecision;
|
public readonly int GatherBiasPrecision;
|
||||||
|
|
||||||
public readonly ulong MaximumGpuMemory;
|
|
||||||
|
|
||||||
public Capabilities(
|
public Capabilities(
|
||||||
TargetApi api,
|
TargetApi api,
|
||||||
string vendorName,
|
string vendorName,
|
||||||
SystemMemoryType memoryType,
|
|
||||||
bool hasFrontFacingBug,
|
bool hasFrontFacingBug,
|
||||||
bool hasVectorIndexingBug,
|
bool hasVectorIndexingBug,
|
||||||
bool needsFragmentOutputSpecialization,
|
bool needsFragmentOutputSpecialization,
|
||||||
|
@ -105,7 +93,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
bool supportsMismatchingViewFormat,
|
bool supportsMismatchingViewFormat,
|
||||||
bool supportsCubemapView,
|
bool supportsCubemapView,
|
||||||
bool supportsNonConstantTextureOffset,
|
bool supportsNonConstantTextureOffset,
|
||||||
bool supportsQuads,
|
|
||||||
bool supportsSeparateSampler,
|
bool supportsSeparateSampler,
|
||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
bool supportsShaderBarrierDivergence,
|
bool supportsShaderBarrierDivergence,
|
||||||
|
@ -118,12 +105,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
bool supportsViewportSwizzle,
|
bool supportsViewportSwizzle,
|
||||||
bool supportsIndirectParameters,
|
bool supportsIndirectParameters,
|
||||||
bool supportsDepthClipControl,
|
bool supportsDepthClipControl,
|
||||||
int uniformBufferSetIndex,
|
|
||||||
int storageBufferSetIndex,
|
|
||||||
int textureSetIndex,
|
|
||||||
int imageSetIndex,
|
|
||||||
int extraSetBaseIndex,
|
|
||||||
int maximumExtraSets,
|
|
||||||
uint maximumUniformBuffersPerStage,
|
uint maximumUniformBuffersPerStage,
|
||||||
uint maximumStorageBuffersPerStage,
|
uint maximumStorageBuffersPerStage,
|
||||||
uint maximumTexturesPerStage,
|
uint maximumTexturesPerStage,
|
||||||
|
@ -133,12 +114,10 @@ namespace Ryujinx.Graphics.GAL
|
||||||
int shaderSubgroupSize,
|
int shaderSubgroupSize,
|
||||||
int storageBufferOffsetAlignment,
|
int storageBufferOffsetAlignment,
|
||||||
int textureBufferOffsetAlignment,
|
int textureBufferOffsetAlignment,
|
||||||
int gatherBiasPrecision,
|
int gatherBiasPrecision)
|
||||||
ulong maximumGpuMemory)
|
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
VendorName = vendorName;
|
VendorName = vendorName;
|
||||||
MemoryType = memoryType;
|
|
||||||
HasFrontFacingBug = hasFrontFacingBug;
|
HasFrontFacingBug = hasFrontFacingBug;
|
||||||
HasVectorIndexingBug = hasVectorIndexingBug;
|
HasVectorIndexingBug = hasVectorIndexingBug;
|
||||||
NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
|
NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
|
||||||
|
@ -167,7 +146,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
||||||
SupportsCubemapView = supportsCubemapView;
|
SupportsCubemapView = supportsCubemapView;
|
||||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||||
SupportsQuads = supportsQuads;
|
|
||||||
SupportsSeparateSampler = supportsSeparateSampler;
|
SupportsSeparateSampler = supportsSeparateSampler;
|
||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||||
|
@ -180,12 +158,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||||
SupportsIndirectParameters = supportsIndirectParameters;
|
SupportsIndirectParameters = supportsIndirectParameters;
|
||||||
SupportsDepthClipControl = supportsDepthClipControl;
|
SupportsDepthClipControl = supportsDepthClipControl;
|
||||||
UniformBufferSetIndex = uniformBufferSetIndex;
|
|
||||||
StorageBufferSetIndex = storageBufferSetIndex;
|
|
||||||
TextureSetIndex = textureSetIndex;
|
|
||||||
ImageSetIndex = imageSetIndex;
|
|
||||||
ExtraSetBaseIndex = extraSetBaseIndex;
|
|
||||||
MaximumExtraSets = maximumExtraSets;
|
|
||||||
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
||||||
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
|
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
|
||||||
MaximumTexturesPerStage = maximumTexturesPerStage;
|
MaximumTexturesPerStage = maximumTexturesPerStage;
|
||||||
|
@ -196,7 +168,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||||
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
||||||
GatherBiasPrecision = gatherBiasPrecision;
|
GatherBiasPrecision = gatherBiasPrecision;
|
||||||
MaximumGpuMemory = maximumGpuMemory;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -711,36 +711,5 @@ namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
return format.IsUint() || format.IsSint();
|
return format.IsUint() || format.IsSint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture format is a float or sRGB color format.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Does not include normalized, compressed or depth formats.
|
|
||||||
/// Float and sRGB formats do not participate in logical operations.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="format">Texture format</param>
|
|
||||||
/// <returns>True if the format is a float or sRGB color format, false otherwise</returns>
|
|
||||||
public static bool IsFloatOrSrgb(this Format format)
|
|
||||||
{
|
|
||||||
switch (format)
|
|
||||||
{
|
|
||||||
case Format.R8G8B8A8Srgb:
|
|
||||||
case Format.B8G8R8A8Srgb:
|
|
||||||
case Format.R16Float:
|
|
||||||
case Format.R16G16Float:
|
|
||||||
case Format.R16G16B16Float:
|
|
||||||
case Format.R16G16B16A16Float:
|
|
||||||
case Format.R32Float:
|
|
||||||
case Format.R32G32Float:
|
|
||||||
case Format.R32G32B32Float:
|
|
||||||
case Format.R32G32B32A32Float:
|
|
||||||
case Format.R11G11B10Float:
|
|
||||||
case Format.R9G9B9E5Float:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public interface IImageArray : IDisposable
|
public interface IImageArray
|
||||||
{
|
{
|
||||||
|
void SetFormats(int index, Format[] imageFormats);
|
||||||
void SetImages(int index, ITexture[] images);
|
void SetImages(int index, ITexture[] images);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,9 +58,8 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
||||||
|
|
||||||
void SetImage(ShaderStage stage, int binding, ITexture texture);
|
void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
|
||||||
void SetImageArray(ShaderStage stage, int binding, IImageArray array);
|
void SetImageArray(ShaderStage stage, int binding, IImageArray array);
|
||||||
void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
|
|
||||||
|
|
||||||
void SetLineParameters(float width, bool smooth);
|
void SetLineParameters(float width, bool smooth);
|
||||||
|
|
||||||
|
@ -92,7 +91,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
|
void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
|
||||||
void SetTextureArray(ShaderStage stage, int binding, ITextureArray array);
|
void SetTextureArray(ShaderStage stage, int binding, ITextureArray array);
|
||||||
void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array);
|
|
||||||
|
|
||||||
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
|
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
|
||||||
void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers);
|
void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers);
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.GAL
|
||||||
void BackgroundContextAction(Action action, bool alwaysBackground = false);
|
void BackgroundContextAction(Action action, bool alwaysBackground = false);
|
||||||
|
|
||||||
BufferHandle CreateBuffer(int size, BufferAccess access = BufferAccess.Default);
|
BufferHandle CreateBuffer(int size, BufferAccess access = BufferAccess.Default);
|
||||||
|
BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint);
|
||||||
BufferHandle CreateBuffer(nint pointer, int size);
|
BufferHandle CreateBuffer(nint pointer, int size);
|
||||||
BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers);
|
BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.Common.Memory;
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
|
@ -18,30 +18,30 @@ namespace Ryujinx.Graphics.GAL
|
||||||
PinnedSpan<byte> GetData(int layer, int level);
|
PinnedSpan<byte> GetData(int layer, int level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
void SetData(MemoryOwner<byte> data);
|
void SetData(IMemoryOwner<byte> data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
void SetData(MemoryOwner<byte> data, int layer, int level);
|
void SetData(IMemoryOwner<byte> data, int layer, int level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
/// <param name="region">Target sub-region of the texture to update</param>
|
/// <param name="region">Target sub-region of the texture to update</param>
|
||||||
void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
|
void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
|
||||||
|
|
||||||
void SetStorage(BufferRange buffer);
|
void SetStorage(BufferRange buffer);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public interface ITextureArray : IDisposable
|
public interface ITextureArray
|
||||||
{
|
{
|
||||||
void SetSamplers(int index, ISampler[] samplers);
|
void SetSamplers(int index, ISampler[] samplers);
|
||||||
void SetTextures(int index, ITexture[] textures);
|
void SetTextures(int index, ITexture[] textures);
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
}
|
}
|
||||||
|
|
||||||
Register<ActionCommand>(CommandType.Action);
|
Register<ActionCommand>(CommandType.Action);
|
||||||
|
Register<CreateBufferCommand>(CommandType.CreateBuffer);
|
||||||
Register<CreateBufferAccessCommand>(CommandType.CreateBufferAccess);
|
Register<CreateBufferAccessCommand>(CommandType.CreateBufferAccess);
|
||||||
Register<CreateBufferSparseCommand>(CommandType.CreateBufferSparse);
|
Register<CreateBufferSparseCommand>(CommandType.CreateBufferSparse);
|
||||||
Register<CreateHostBufferCommand>(CommandType.CreateHostBuffer);
|
Register<CreateHostBufferCommand>(CommandType.CreateHostBuffer);
|
||||||
|
@ -66,7 +67,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose);
|
Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose);
|
||||||
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
||||||
|
|
||||||
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
|
Register<ImageArraySetFormatsCommand>(CommandType.ImageArraySetFormats);
|
||||||
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
||||||
|
|
||||||
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
|
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
|
||||||
|
@ -88,7 +89,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
Register<TextureSetDataSliceRegionCommand>(CommandType.TextureSetDataSliceRegion);
|
Register<TextureSetDataSliceRegionCommand>(CommandType.TextureSetDataSliceRegion);
|
||||||
Register<TextureSetStorageCommand>(CommandType.TextureSetStorage);
|
Register<TextureSetStorageCommand>(CommandType.TextureSetStorage);
|
||||||
|
|
||||||
Register<TextureArrayDisposeCommand>(CommandType.TextureArrayDispose);
|
|
||||||
Register<TextureArraySetSamplersCommand>(CommandType.TextureArraySetSamplers);
|
Register<TextureArraySetSamplersCommand>(CommandType.TextureArraySetSamplers);
|
||||||
Register<TextureArraySetTexturesCommand>(CommandType.TextureArraySetTextures);
|
Register<TextureArraySetTexturesCommand>(CommandType.TextureArraySetTextures);
|
||||||
|
|
||||||
|
@ -125,7 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
Register<SetUniformBuffersCommand>(CommandType.SetUniformBuffers);
|
Register<SetUniformBuffersCommand>(CommandType.SetUniformBuffers);
|
||||||
Register<SetImageCommand>(CommandType.SetImage);
|
Register<SetImageCommand>(CommandType.SetImage);
|
||||||
Register<SetImageArrayCommand>(CommandType.SetImageArray);
|
Register<SetImageArrayCommand>(CommandType.SetImageArray);
|
||||||
Register<SetImageArraySeparateCommand>(CommandType.SetImageArraySeparate);
|
|
||||||
Register<SetIndexBufferCommand>(CommandType.SetIndexBuffer);
|
Register<SetIndexBufferCommand>(CommandType.SetIndexBuffer);
|
||||||
Register<SetLineParametersCommand>(CommandType.SetLineParameters);
|
Register<SetLineParametersCommand>(CommandType.SetLineParameters);
|
||||||
Register<SetLogicOpStateCommand>(CommandType.SetLogicOpState);
|
Register<SetLogicOpStateCommand>(CommandType.SetLogicOpState);
|
||||||
|
@ -143,7 +142,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
Register<SetStencilTestCommand>(CommandType.SetStencilTest);
|
Register<SetStencilTestCommand>(CommandType.SetStencilTest);
|
||||||
Register<SetTextureAndSamplerCommand>(CommandType.SetTextureAndSampler);
|
Register<SetTextureAndSamplerCommand>(CommandType.SetTextureAndSampler);
|
||||||
Register<SetTextureArrayCommand>(CommandType.SetTextureArray);
|
Register<SetTextureArrayCommand>(CommandType.SetTextureArray);
|
||||||
Register<SetTextureArraySeparateCommand>(CommandType.SetTextureArraySeparate);
|
|
||||||
Register<SetUserClipDistanceCommand>(CommandType.SetUserClipDistance);
|
Register<SetUserClipDistanceCommand>(CommandType.SetUserClipDistance);
|
||||||
Register<SetVertexAttribsCommand>(CommandType.SetVertexAttribs);
|
Register<SetVertexAttribsCommand>(CommandType.SetVertexAttribs);
|
||||||
Register<SetVertexBuffersCommand>(CommandType.SetVertexBuffers);
|
Register<SetVertexBuffersCommand>(CommandType.SetVertexBuffers);
|
||||||
|
|
|
@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
enum CommandType : byte
|
enum CommandType : byte
|
||||||
{
|
{
|
||||||
Action,
|
Action,
|
||||||
|
CreateBuffer,
|
||||||
CreateBufferAccess,
|
CreateBufferAccess,
|
||||||
CreateBufferSparse,
|
CreateBufferSparse,
|
||||||
CreateHostBuffer,
|
CreateHostBuffer,
|
||||||
|
@ -26,7 +27,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
CounterEventDispose,
|
CounterEventDispose,
|
||||||
CounterEventFlush,
|
CounterEventFlush,
|
||||||
|
|
||||||
ImageArrayDispose,
|
ImageArraySetFormats,
|
||||||
ImageArraySetImages,
|
ImageArraySetImages,
|
||||||
|
|
||||||
ProgramDispose,
|
ProgramDispose,
|
||||||
|
@ -48,7 +49,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
TextureSetDataSliceRegion,
|
TextureSetDataSliceRegion,
|
||||||
TextureSetStorage,
|
TextureSetStorage,
|
||||||
|
|
||||||
TextureArrayDispose,
|
|
||||||
TextureArraySetSamplers,
|
TextureArraySetSamplers,
|
||||||
TextureArraySetTextures,
|
TextureArraySetTextures,
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
SetUniformBuffers,
|
SetUniformBuffers,
|
||||||
SetImage,
|
SetImage,
|
||||||
SetImageArray,
|
SetImageArray,
|
||||||
SetImageArraySeparate,
|
|
||||||
SetIndexBuffer,
|
SetIndexBuffer,
|
||||||
SetLineParameters,
|
SetLineParameters,
|
||||||
SetLogicOpState,
|
SetLogicOpState,
|
||||||
|
@ -103,7 +102,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
SetStencilTest,
|
SetStencilTest,
|
||||||
SetTextureAndSampler,
|
SetTextureAndSampler,
|
||||||
SetTextureArray,
|
SetTextureArray,
|
||||||
SetTextureArraySeparate,
|
|
||||||
SetUserClipDistance,
|
SetUserClipDistance,
|
||||||
SetVertexAttribs,
|
SetVertexAttribs,
|
||||||
SetVertexBuffers,
|
SetVertexBuffers,
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray
|
|
||||||
{
|
|
||||||
struct ImageArrayDisposeCommand : IGALCommand, IGALCommand<ImageArrayDisposeCommand>
|
|
||||||
{
|
|
||||||
public readonly CommandType CommandType => CommandType.ImageArrayDispose;
|
|
||||||
private TableRef<ThreadedImageArray> _imageArray;
|
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedImageArray> imageArray)
|
|
||||||
{
|
|
||||||
_imageArray = imageArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Run(ref ImageArrayDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
|
||||||
{
|
|
||||||
command._imageArray.Get(threaded).Base.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray
|
||||||
|
{
|
||||||
|
struct ImageArraySetFormatsCommand : IGALCommand, IGALCommand<ImageArraySetFormatsCommand>
|
||||||
|
{
|
||||||
|
public readonly CommandType CommandType => CommandType.ImageArraySetFormats;
|
||||||
|
private TableRef<ThreadedImageArray> _imageArray;
|
||||||
|
private int _index;
|
||||||
|
private TableRef<Format[]> _imageFormats;
|
||||||
|
|
||||||
|
public void Set(TableRef<ThreadedImageArray> imageArray, int index, TableRef<Format[]> imageFormats)
|
||||||
|
{
|
||||||
|
_imageArray = imageArray;
|
||||||
|
_index = index;
|
||||||
|
_imageFormats = imageFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(ref ImageArraySetFormatsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
|
{
|
||||||
|
ThreadedImageArray imageArray = command._imageArray.Get(threaded);
|
||||||
|
imageArray.Base.SetFormats(command._index, command._imageFormats.Get(threaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
|
||||||
|
{
|
||||||
|
struct CreateBufferCommand : IGALCommand, IGALCommand<CreateBufferCommand>
|
||||||
|
{
|
||||||
|
public readonly CommandType CommandType => CommandType.CreateBuffer;
|
||||||
|
private BufferHandle _threadedHandle;
|
||||||
|
private int _size;
|
||||||
|
private BufferAccess _access;
|
||||||
|
private BufferHandle _storageHint;
|
||||||
|
|
||||||
|
public void Set(BufferHandle threadedHandle, int size, BufferAccess access, BufferHandle storageHint)
|
||||||
|
{
|
||||||
|
_threadedHandle = threadedHandle;
|
||||||
|
_size = size;
|
||||||
|
_access = access;
|
||||||
|
_storageHint = storageHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
|
{
|
||||||
|
BufferHandle hint = BufferHandle.Null;
|
||||||
|
|
||||||
|
if (command._storageHint != BufferHandle.Null)
|
||||||
|
{
|
||||||
|
hint = threaded.Buffers.MapBuffer(command._storageHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, command._access, hint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
|
||||||
using Ryujinx.Graphics.Shader;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|
||||||
{
|
|
||||||
struct SetImageArraySeparateCommand : IGALCommand, IGALCommand<SetImageArraySeparateCommand>
|
|
||||||
{
|
|
||||||
public readonly CommandType CommandType => CommandType.SetImageArraySeparate;
|
|
||||||
private ShaderStage _stage;
|
|
||||||
private int _setIndex;
|
|
||||||
private TableRef<IImageArray> _array;
|
|
||||||
|
|
||||||
public void Set(ShaderStage stage, int setIndex, TableRef<IImageArray> array)
|
|
||||||
{
|
|
||||||
_stage = stage;
|
|
||||||
_setIndex = setIndex;
|
|
||||||
_array = array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Run(ref SetImageArraySeparateCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
|
||||||
{
|
|
||||||
renderer.Pipeline.SetImageArraySeparate(command._stage, command._setIndex, command._array.GetAs<ThreadedImageArray>(threaded)?.Base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,17 +10,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||||
private ShaderStage _stage;
|
private ShaderStage _stage;
|
||||||
private int _binding;
|
private int _binding;
|
||||||
private TableRef<ITexture> _texture;
|
private TableRef<ITexture> _texture;
|
||||||
|
private Format _imageFormat;
|
||||||
|
|
||||||
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture)
|
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, Format imageFormat)
|
||||||
{
|
{
|
||||||
_stage = stage;
|
_stage = stage;
|
||||||
_binding = binding;
|
_binding = binding;
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
|
_imageFormat = imageFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base);
|
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
|
||||||
using Ryujinx.Graphics.Shader;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|
||||||
{
|
|
||||||
struct SetTextureArraySeparateCommand : IGALCommand, IGALCommand<SetTextureArraySeparateCommand>
|
|
||||||
{
|
|
||||||
public readonly CommandType CommandType => CommandType.SetTextureArraySeparate;
|
|
||||||
private ShaderStage _stage;
|
|
||||||
private int _setIndex;
|
|
||||||
private TableRef<ITextureArray> _array;
|
|
||||||
|
|
||||||
public void Set(ShaderStage stage, int setIndex, TableRef<ITextureArray> array)
|
|
||||||
{
|
|
||||||
_stage = stage;
|
|
||||||
_setIndex = setIndex;
|
|
||||||
_array = array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Run(ref SetTextureArraySeparateCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
|
||||||
{
|
|
||||||
renderer.Pipeline.SetTextureArraySeparate(command._stage, command._setIndex, command._array.GetAs<ThreadedTextureArray>(threaded)?.Base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
|
@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetData;
|
public readonly CommandType CommandType => CommandType.TextureSetData;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<MemoryOwner<byte>> _data;
|
private TableRef<IMemoryOwner<byte>> _data;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
|
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<MemoryOwner<byte>> _data;
|
private TableRef<IMemoryOwner<byte>> _data;
|
||||||
private int _layer;
|
private int _layer;
|
||||||
private int _level;
|
private int _level;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data, int layer, int level)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
|
@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
|
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<MemoryOwner<byte>> _data;
|
private TableRef<IMemoryOwner<byte>> _data;
|
||||||
private int _layer;
|
private int _layer;
|
||||||
private int _level;
|
private int _level;
|
||||||
private Rectangle<int> _region;
|
private Rectangle<int> _region;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.TextureArray
|
|
||||||
{
|
|
||||||
struct TextureArrayDisposeCommand : IGALCommand, IGALCommand<TextureArrayDisposeCommand>
|
|
||||||
{
|
|
||||||
public readonly CommandType CommandType => CommandType.TextureArrayDispose;
|
|
||||||
private TableRef<ThreadedTextureArray> _textureArray;
|
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTextureArray> textureArray)
|
|
||||||
{
|
|
||||||
_textureArray = textureArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Run(ref TextureArrayDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
|
||||||
{
|
|
||||||
command._textureArray.Get(threaded).Base.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,9 +21,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
return new TableRef<T>(_renderer, reference);
|
return new TableRef<T>(_renderer, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void SetFormats(int index, Format[] imageFormats)
|
||||||
{
|
{
|
||||||
_renderer.New<ImageArrayDisposeCommand>().Set(Ref(this));
|
_renderer.New<ImageArraySetFormatsCommand>().Set(Ref(this), index, Ref(imageFormats));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
{
|
{
|
||||||
|
@ -111,21 +111,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
|
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
|
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
|
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
|
|
|
@ -22,12 +22,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
return new TableRef<T>(_renderer, reference);
|
return new TableRef<T>(_renderer, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_renderer.New<TextureArrayDisposeCommand>().Set(Ref(this));
|
|
||||||
_renderer.QueueCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSamplers(int index, ISampler[] samplers)
|
public void SetSamplers(int index, ISampler[] samplers)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureArraySetSamplersCommand>().Set(Ref(this), index, Ref(samplers.ToArray()));
|
_renderer.New<TextureArraySetSamplersCommand>().Set(Ref(this), index, Ref(samplers.ToArray()));
|
||||||
|
|
|
@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture texture)
|
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
||||||
{
|
{
|
||||||
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture));
|
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture), imageFormat);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +189,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
|
|
||||||
{
|
|
||||||
_renderer.New<SetImageArraySeparateCommand>().Set(stage, setIndex, Ref(array));
|
|
||||||
_renderer.QueueCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
||||||
{
|
{
|
||||||
_renderer.New<SetIndexBufferCommand>().Set(buffer, type);
|
_renderer.New<SetIndexBufferCommand>().Set(buffer, type);
|
||||||
|
@ -303,12 +297,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
|
|
||||||
{
|
|
||||||
_renderer.New<SetTextureArraySeparateCommand>().Set(stage, setIndex, Ref(array));
|
|
||||||
_renderer.QueueCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
|
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
|
||||||
{
|
{
|
||||||
_renderer.New<SetTransformFeedbackBuffersCommand>().Set(_renderer.CopySpan(buffers));
|
_renderer.New<SetTransformFeedbackBuffersCommand>().Set(_renderer.CopySpan(buffers));
|
||||||
|
|
|
@ -272,6 +272,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint)
|
||||||
|
{
|
||||||
|
BufferHandle handle = Buffers.CreateBufferHandle();
|
||||||
|
New<CreateBufferCommand>().Set(handle, size, access, storageHint);
|
||||||
|
QueueCommand();
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(nint pointer, int size)
|
public BufferHandle CreateBuffer(nint pointer, int size)
|
||||||
{
|
{
|
||||||
BufferHandle handle = Buffers.CreateBufferHandle();
|
BufferHandle handle = Buffers.CreateBufferHandle();
|
||||||
|
|
|
@ -74,15 +74,13 @@ namespace Ryujinx.Graphics.GAL
|
||||||
public int ArrayLength { get; }
|
public int ArrayLength { get; }
|
||||||
public ResourceType Type { get; }
|
public ResourceType Type { get; }
|
||||||
public ResourceStages Stages { get; }
|
public ResourceStages Stages { get; }
|
||||||
public bool Write { get; }
|
|
||||||
|
|
||||||
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write)
|
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages)
|
||||||
{
|
{
|
||||||
Binding = binding;
|
Binding = binding;
|
||||||
ArrayLength = arrayLength;
|
ArrayLength = arrayLength;
|
||||||
Type = type;
|
Type = type;
|
||||||
Stages = stages;
|
Stages = stages;
|
||||||
Write = write;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.GAL
|
|
||||||
{
|
|
||||||
public enum SystemMemoryType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The backend manages the ownership of memory. This mode never supports host imported memory.
|
|
||||||
/// </summary>
|
|
||||||
BackendManaged,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Device memory has similar performance to host memory, usually because it's shared between CPU/GPU.
|
|
||||||
/// Use host memory whenever possible.
|
|
||||||
/// </summary>
|
|
||||||
UnifiedMemory,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU storage to host memory goes though a slow interconnect, but it would still be preferable to use it if the data is flushed back often.
|
|
||||||
/// Assumes constant buffer access to host memory is rather fast.
|
|
||||||
/// </summary>
|
|
||||||
DedicatedMemory,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU storage to host memory goes though a slow interconnect, that is very slow when doing access from storage.
|
|
||||||
/// When frequently accessed, copy buffers to host memory using DMA.
|
|
||||||
/// Assumes constant buffer access to host memory is rather fast.
|
|
||||||
/// </summary>
|
|
||||||
DedicatedMemorySlowStorage
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
|
@ -112,6 +113,25 @@ namespace Ryujinx.Graphics.GAL
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetLevelsClamped()
|
||||||
|
{
|
||||||
|
int maxSize = Width;
|
||||||
|
|
||||||
|
if (Target != Target.Texture1D &&
|
||||||
|
Target != Target.Texture1DArray)
|
||||||
|
{
|
||||||
|
maxSize = Math.Max(maxSize, Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
maxSize = Math.Max(maxSize, Depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
|
||||||
|
return Math.Min(Levels, maxLevels);
|
||||||
|
}
|
||||||
|
|
||||||
private static int GetLevelSize(int size, int level)
|
private static int GetLevelSize(int size, int level)
|
||||||
{
|
{
|
||||||
return Math.Max(1, size >> level);
|
return Math.Max(1, size >> level);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue