Compare commits

..

2 commits

Author SHA1 Message Date
TSR Berry
b51d57b642
Fix fetch failure when iterating over PRs 2024-05-01 18:13:09 +02:00
dependabot[bot]
54c16f4052
ci: bump actions/github-script from 6 to 7
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 18:11:17 +02:00
418 changed files with 5100 additions and 14888 deletions

View file

@ -23,7 +23,7 @@ body:
attributes:
label: Log file
description: A log file will help our developers to better diagnose and fix the issue.
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. 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:
required: true
- type: input
@ -83,4 +83,4 @@ body:
- Additional info about your environment:
- Any other information relevant to your issue.
validations:
required: false
required: false

View file

@ -1,7 +1,6 @@
name: Feature Request
description: Suggest a new feature for Ryujinx.
title: "[Feature Request]"
labels: enhancement
body:
- type: textarea
id: overview

View file

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.repo;
@ -19,7 +19,7 @@ jobs:
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
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 (const pull of data) {
if (pull.head.sha === pull_head_sha) {
@ -68,4 +68,4 @@ jobs:
} else {
core.info(`Creating a comment`);
await github.rest.issues.createComment({repo, owner, issue_number, body});
}
}

View file

@ -34,7 +34,7 @@ jobs:
shell: bash
- name: Create tag
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
github.rest.git.createRef({

View file

@ -8,21 +8,21 @@
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.16" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.16" />
<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="DynamicData" Version="9.0.4" />
<PackageVersion Include="DynamicData" Version="8.4.1" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.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.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="NetCoreServer" Version="8.0.7" />
<PackageVersion Include="NUnit" Version="3.13.3" />
@ -39,14 +39,14 @@
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
<PackageVersion Include="System.Management" Version="8.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>
</Project>

View file

@ -36,8 +36,8 @@
## Compatibility
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
As of October 2023, Ryujinx has been tested on approximately 4,200 titles;
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable.
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).

View file

@ -87,8 +87,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -251,10 +249,6 @@ Global
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -4,8 +4,6 @@
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>

View file

@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64
long originalPosition = _stream.Position;
_stream.Seek(0, SeekOrigin.Begin);
_stream.ReadExactly(code, 0, code.Length);
_stream.Read(code, 0, code.Length);
_stream.Seek(originalPosition, SeekOrigin.Begin);
RelocInfo relocInfo;

View file

@ -251,20 +251,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
}
}
// If this is a copy destination variable, we prefer the register used for the copy source.
// If the register is available, then the copy can be eliminated later as both source
// and destination will use the same register.
int selectedReg;
if (current.TryGetCopySourceRegister(out int preferredReg) && freePositions[preferredReg] >= current.GetEnd())
{
selectedReg = preferredReg;
}
else
{
selectedReg = GetHighestValueIndex(freePositions);
}
int selectedReg = GetHighestValueIndex(freePositions);
int selectedNextUse = freePositions[selectedReg];
// 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;
@ -811,12 +798,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
// The "visited" state is stored in the MSB of the local's value.
const ulong VisitedMask = 1ul << 63;
static bool IsVisited(Operand local)
bool IsVisited(Operand local)
{
return (local.GetValueUnsafe() & VisitedMask) != 0;
}
static void SetVisited(Operand local)
void SetVisited(Operand local)
{
local.GetValueUnsafe() |= VisitedMask;
}
@ -839,25 +826,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
dest.NumberLocal(_intervals.Count);
LiveInterval interval = new LiveInterval(dest);
_intervals.Add(interval);
_intervals.Add(new LiveInterval(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()]);
}
}
}
}
}

View file

@ -19,7 +19,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
public LiveRange CurrRange;
public LiveInterval Parent;
public LiveInterval CopySource;
public UseList Uses;
public LiveIntervalList Children;
@ -38,7 +37,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
private ref LiveRange CurrRange => ref _data->CurrRange;
private ref LiveRange PrevRange => ref _data->PrevRange;
private ref LiveInterval Parent => ref _data->Parent;
private ref LiveInterval CopySource => ref _data->CopySource;
private ref UseList Uses => ref _data->Uses;
private ref LiveIntervalList Children => ref _data->Children;
@ -80,25 +78,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
Register = register;
}
public void SetCopySource(LiveInterval copySource)
{
CopySource = copySource;
}
public bool TryGetCopySourceRegister(out int copySourceRegIndex)
{
if (CopySource._data != null)
{
copySourceRegIndex = CopySource.Register.Index;
return true;
}
copySourceRegIndex = 0;
return false;
}
public void Reset()
{
PrevRange = default;

View file

@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
_stream.ReadExactly(buffer);
_stream.Read(buffer);
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
codeStream.Write(buffer);

View file

@ -746,7 +746,6 @@ namespace ARMeilleure.Decoders
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create);
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
@ -823,10 +822,6 @@ namespace ARMeilleure.Decoders
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
@ -1012,8 +1007,6 @@ namespace ARMeilleure.Decoders
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
@ -1035,10 +1028,8 @@ namespace ARMeilleure.Decoders
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 encoding.
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
@ -1063,7 +1054,6 @@ namespace ARMeilleure.Decoders
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
SetAsimd("111100111x110010xxxx00000xx0xxxx", InstName.Vswp, InstEmit32.Vswp, OpCode32Simd.Create, OpCode32Simd.CreateT32);
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);

View file

@ -2,8 +2,6 @@ using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
@ -292,16 +290,6 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res);
}
public static void Qadd16(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitSigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false);
}));
}
public static void Rbit(ArmEmitterContext context)
{
Operand m = GetAluM(context);
@ -570,46 +558,6 @@ namespace ARMeilleure.Instructions
EmitHsub8(context, unsigned: true);
}
public static void Uqadd16(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqadd(context, d, context.Add(n, m), 16);
}));
}
public static void Uqadd8(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqadd(context, d, context.Add(n, m), 8);
}));
}
public static void Uqsub16(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
}));
}
public static void Uqsub8(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
}));
}
public static void Usat(ArmEmitterContext context)
{
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
@ -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)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;

View file

@ -1246,33 +1246,6 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
}
public static void Vqrdmulh(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
int eSize = 8 << op.Size;
EmitVectorBinaryOpI32(context, (op1, op2) =>
{
if (op.Size == 2)
{
op1 = context.SignExtend32(OperandType.I64, op1);
op2 = context.SignExtend32(OperandType.I64, op2);
}
Operand res = context.Multiply(op1, op2);
res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
res = context.ShiftRightSI(res, Const(eSize - 1));
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
if (op.Size == 2)
{
res = context.ConvertI64ToI32(res);
}
return res;
}, signed: true);
}
public static void Vqsub(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;

View file

@ -191,26 +191,6 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vswp(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.Q)
{
Operand temp = context.Copy(GetVecA32(op.Qd));
context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
context.Copy(GetVecA32(op.Qm), temp);
}
else
{
Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
InsertScalar(context, op.Vm, temp);
}
}
public static void Vtbl(ArmEmitterContext context)
{
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;

View file

@ -106,38 +106,6 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vshll2(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
Operand res = context.VectorZero();
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
if (op.Size == 2)
{
if (op.U)
{
me = context.ZeroExtend32(OperandType.I64, me);
}
else
{
me = context.SignExtend32(OperandType.I64, me);
}
}
me = context.ShiftLeft(me, Const(8 << op.Size));
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vshr(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
@ -162,36 +130,6 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
}
public static void Vsli_I(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
int shift = op.Shift;
int eSize = 8 << op.Size;
ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
Operand res = GetVec(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
Operand neShifted = context.ShiftLeft(me, Const(shift));
Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
Operand deMasked = context.BitwiseAnd(de, Const(mask));
Operand e = context.BitwiseOr(neShifted, deMasked);
res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
}
context.Copy(GetVec(op.Qd), res);
}
public static void Vsra(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;

View file

@ -527,7 +527,6 @@ namespace ARMeilleure.Instructions
Pld,
Pop,
Push,
Qadd16,
Rev,
Revsh,
Rsb,
@ -572,10 +571,6 @@ namespace ARMeilleure.Instructions
Umaal,
Umlal,
Umull,
Uqadd16,
Uqadd8,
Uqsub16,
Uqsub8,
Usat,
Usat16,
Usub8,
@ -650,7 +645,6 @@ namespace ARMeilleure.Instructions
Vqdmulh,
Vqmovn,
Vqmovun,
Vqrdmulh,
Vqrshrn,
Vqrshrun,
Vqshrn,
@ -672,7 +666,6 @@ namespace ARMeilleure.Instructions
Vshll,
Vshr,
Vshrn,
Vsli,
Vst1,
Vst2,
Vst3,
@ -689,7 +682,6 @@ namespace ARMeilleure.Instructions
Vsub,
Vsubl,
Vsubw,
Vswp,
Vtbl,
Vtrn,
Vtst,

View file

@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
private int[] _postOrderMap;
public int LocalsCount { get; private set; }
public BasicBlock Entry { get; private set; }
public BasicBlock Entry { get; }
public IntrusiveList<BasicBlock> Blocks { get; }
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
public int[] PostOrderMap => _postOrderMap;
@ -34,15 +34,6 @@ namespace ARMeilleure.Translation
return result;
}
public void UpdateEntry(BasicBlock newEntry)
{
newEntry.AddSuccessor(Entry);
Entry = newEntry;
Blocks.AddFirst(newEntry);
Update();
}
public void Update()
{
RemoveUnreachableBlocks(Blocks);

View file

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@ -10,10 +11,11 @@ namespace ARMeilleure.Translation
public IntPtr FuncPtr { get; }
public DelegateInfo(Delegate dlg, IntPtr funcPtr)
public DelegateInfo(Delegate dlg)
{
_dlg = dlg;
FuncPtr = funcPtr;
FuncPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(dlg);
}
}
}

View file

@ -3,7 +3,6 @@ using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@ -65,11 +64,11 @@ namespace ARMeilleure.Translation
return index;
}
private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr)
private static void SetDelegateInfo(Delegate dlg)
{
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)
@ -83,353 +82,179 @@ namespace ARMeilleure.Translation
{
_delegates = new SortedList<string, DelegateInfo>();
var dlgMathAbs = new MathAbs(Math.Abs);
var dlgMathCeiling = new MathCeiling(Math.Ceiling);
var dlgMathFloor = new MathFloor(Math.Floor);
var dlgMathRound = new MathRound(Math.Round);
var dlgMathTruncate = new MathTruncate(Math.Truncate);
SetDelegateInfo(new MathAbs(Math.Abs));
SetDelegateInfo(new MathCeiling(Math.Ceiling));
SetDelegateInfo(new MathFloor(Math.Floor));
SetDelegateInfo(new MathRound(Math.Round));
SetDelegateInfo(new MathTruncate(Math.Truncate));
var dlgMathFAbs = new MathFAbs(MathF.Abs);
var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling);
var dlgMathFFloor = new MathFFloor(MathF.Floor);
var dlgMathFRound = new MathFRound(MathF.Round);
var dlgMathFTruncate = new MathFTruncate(MathF.Truncate);
SetDelegateInfo(new MathFAbs(MathF.Abs));
SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
SetDelegateInfo(new MathFFloor(MathF.Floor));
SetDelegateInfo(new MathFRound(MathF.Round));
SetDelegateInfo(new MathFTruncate(MathF.Truncate));
var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break);
var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization);
var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit);
var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0);
var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0);
var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0);
var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0);
var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0);
var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress);
var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine);
var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte);
var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16);
var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32);
var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64);
var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128);
var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking);
var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall);
var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess);
var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined);
var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte);
var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16);
var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32);
var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64);
var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128);
SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns);
var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros);
var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b);
var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb);
var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch);
var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw);
var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx);
var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h);
var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w);
var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x);
var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt);
var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt);
var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate);
var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose);
var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower);
var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority);
var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity);
var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper);
var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns);
var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns);
var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128);
var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32);
var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64);
var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32);
var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64);
var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32);
var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64);
var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32);
var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64);
var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1);
var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2);
var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1);
var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2);
var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64);
var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1);
var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2);
var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3);
var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4);
var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1);
var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2);
var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3);
var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4);
var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64);
SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert);
var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert);
SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd);
var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only.
var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare);
var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ);
var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only.
var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE);
var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only.
var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT);
var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only.
var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE);
var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only.
var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT);
var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only.
var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv);
var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax);
var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only.
var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum);
var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only.
var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin);
var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only.
var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum);
var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only.
var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul);
var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only.
var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd);
var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only.
var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub);
var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only.
var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX);
var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd);
var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub);
var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate);
var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only.
var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused);
var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX);
var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate);
var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only.
var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused);
var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt);
var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub);
SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert);
SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd);
var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only.
var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare);
var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ);
var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only.
var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE);
var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only.
var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT);
var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only.
var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE);
var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only.
var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT);
var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only.
var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv);
var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax);
var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only.
var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum);
var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only.
var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin);
var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only.
var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum);
var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only.
var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul);
var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only.
var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd);
var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only.
var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub);
var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only.
var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX);
var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd);
var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub);
var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate);
var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only.
var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused);
var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX);
var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate);
var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only.
var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused);
var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt);
var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub);
SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert);
SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate<MathAbs>(dlgMathAbs));
SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate<MathCeiling>(dlgMathCeiling));
SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate<MathFloor>(dlgMathFloor));
SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate<MathRound>(dlgMathRound));
SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate<MathTruncate>(dlgMathTruncate));
SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate<MathFAbs>(dlgMathFAbs));
SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate<MathFCeiling>(dlgMathFCeiling));
SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate<MathFFloor>(dlgMathFFloor));
SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate<MathFRound>(dlgMathFRound));
SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate<MathFTruncate>(dlgMathFTruncate));
SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate<NativeInterfaceBreak>(dlgNativeInterfaceBreak));
SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate<NativeInterfaceCheckSynchronization>(dlgNativeInterfaceCheckSynchronization));
SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate<NativeInterfaceEnqueueForRejit>(dlgNativeInterfaceEnqueueForRejit));
SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntfrqEl0>(dlgNativeInterfaceGetCntfrqEl0));
SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntpctEl0>(dlgNativeInterfaceGetCntpctEl0));
SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntvctEl0>(dlgNativeInterfaceGetCntvctEl0));
SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCtrEl0>(dlgNativeInterfaceGetCtrEl0));
SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetDczidEl0>(dlgNativeInterfaceGetDczidEl0));
SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetFunctionAddress>(dlgNativeInterfaceGetFunctionAddress));
SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate<NativeInterfaceInvalidateCacheLine>(dlgNativeInterfaceInvalidateCacheLine));
SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadByte>(dlgNativeInterfaceReadByte));
SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt16>(dlgNativeInterfaceReadUInt16));
SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt32>(dlgNativeInterfaceReadUInt32));
SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt64>(dlgNativeInterfaceReadUInt64));
SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadVector128>(dlgNativeInterfaceReadVector128));
SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSignalMemoryTracking>(dlgNativeInterfaceSignalMemoryTracking));
SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSupervisorCall>(dlgNativeInterfaceSupervisorCall));
SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate<NativeInterfaceThrowInvalidMemoryAccess>(dlgNativeInterfaceThrowInvalidMemoryAccess));
SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate<NativeInterfaceUndefined>(dlgNativeInterfaceUndefined));
SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteByte>(dlgNativeInterfaceWriteByte));
SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt16>(dlgNativeInterfaceWriteUInt16));
SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt32>(dlgNativeInterfaceWriteUInt32));
SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt64>(dlgNativeInterfaceWriteUInt64));
SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteVector128>(dlgNativeInterfaceWriteVector128));
SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingSigns>(dlgSoftFallbackCountLeadingSigns));
SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingZeros>(dlgSoftFallbackCountLeadingZeros));
SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32b>(dlgSoftFallbackCrc32b));
SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cb>(dlgSoftFallbackCrc32cb));
SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32ch>(dlgSoftFallbackCrc32ch));
SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cw>(dlgSoftFallbackCrc32cw));
SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cx>(dlgSoftFallbackCrc32cx));
SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32h>(dlgSoftFallbackCrc32h));
SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32w>(dlgSoftFallbackCrc32w));
SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32x>(dlgSoftFallbackCrc32x));
SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackDecrypt>(dlgSoftFallbackDecrypt));
SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackEncrypt>(dlgSoftFallbackEncrypt));
SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate<SoftFallbackFixedRotate>(dlgSoftFallbackFixedRotate));
SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashChoose>(dlgSoftFallbackHashChoose));
SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashLower>(dlgSoftFallbackHashLower));
SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashMajority>(dlgSoftFallbackHashMajority));
SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashParity>(dlgSoftFallbackHashParity));
SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashUpper>(dlgSoftFallbackHashUpper));
SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackInverseMixColumns>(dlgSoftFallbackInverseMixColumns));
SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackMixColumns>(dlgSoftFallbackMixColumns));
SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate<SoftFallbackPolynomialMult64_128>(dlgSoftFallbackPolynomialMult64_128));
SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS32>(dlgSoftFallbackSatF32ToS32));
SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS64>(dlgSoftFallbackSatF32ToS64));
SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU32>(dlgSoftFallbackSatF32ToU32));
SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU64>(dlgSoftFallbackSatF32ToU64));
SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS32>(dlgSoftFallbackSatF64ToS32));
SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS64>(dlgSoftFallbackSatF64ToS64));
SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU32>(dlgSoftFallbackSatF64ToU32));
SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU64>(dlgSoftFallbackSatF64ToU64));
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart1>(dlgSoftFallbackSha1SchedulePart1));
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart2>(dlgSoftFallbackSha1SchedulePart2));
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart1>(dlgSoftFallbackSha256SchedulePart1));
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart2>(dlgSoftFallbackSha256SchedulePart2));
SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSignedShrImm64>(dlgSoftFallbackSignedShrImm64));
SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl1>(dlgSoftFallbackTbl1));
SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl2>(dlgSoftFallbackTbl2));
SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl3>(dlgSoftFallbackTbl3));
SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl4>(dlgSoftFallbackTbl4));
SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx1>(dlgSoftFallbackTbx1));
SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx2>(dlgSoftFallbackTbx2));
SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx3>(dlgSoftFallbackTbx3));
SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx4>(dlgSoftFallbackTbx4));
SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackUnsignedShrImm64>(dlgSoftFallbackUnsignedShrImm64));
SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_32FPConvert>(dlgSoftFloat16_32FPConvert));
SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_64FPConvert>(dlgSoftFloat16_64FPConvert));
SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAdd>(dlgSoftFloat32FPAdd));
SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAddFpscr>(dlgSoftFloat32FPAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompare>(dlgSoftFloat32FPCompare));
SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQ>(dlgSoftFloat32FPCompareEQ));
SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQFpscr>(dlgSoftFloat32FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGE>(dlgSoftFloat32FPCompareGE));
SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGEFpscr>(dlgSoftFloat32FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGT>(dlgSoftFloat32FPCompareGT));
SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGTFpscr>(dlgSoftFloat32FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLE>(dlgSoftFloat32FPCompareLE));
SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLEFpscr>(dlgSoftFloat32FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLT>(dlgSoftFloat32FPCompareLT));
SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLTFpscr>(dlgSoftFloat32FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPDiv>(dlgSoftFloat32FPDiv));
SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMax>(dlgSoftFloat32FPMax));
SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxFpscr>(dlgSoftFloat32FPMaxFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNum>(dlgSoftFloat32FPMaxNum));
SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNumFpscr>(dlgSoftFloat32FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMin>(dlgSoftFloat32FPMin));
SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinFpscr>(dlgSoftFloat32FPMinFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNum>(dlgSoftFloat32FPMinNum));
SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNumFpscr>(dlgSoftFloat32FPMinNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMul>(dlgSoftFloat32FPMul));
SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulFpscr>(dlgSoftFloat32FPMulFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAdd>(dlgSoftFloat32FPMulAdd));
SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAddFpscr>(dlgSoftFloat32FPMulAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSub>(dlgSoftFloat32FPMulSub));
SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSubFpscr>(dlgSoftFloat32FPMulSubFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulX>(dlgSoftFloat32FPMulX));
SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulAdd>(dlgSoftFloat32FPNegMulAdd));
SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulSub>(dlgSoftFloat32FPNegMulSub));
SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimate>(dlgSoftFloat32FPRecipEstimate));
SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimateFpscr>(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStep>(dlgSoftFloat32FPRecipStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStepFused>(dlgSoftFloat32FPRecipStepFused));
SetDelegateInfo(dlgSoftFloat32FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecpX>(dlgSoftFloat32FPRecpX));
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimate>(dlgSoftFloat32FPRSqrtEstimate));
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimateFpscr>(dlgSoftFloat32FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStep>(dlgSoftFloat32FPRSqrtStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStepFused>(dlgSoftFloat32FPRSqrtStepFused));
SetDelegateInfo(dlgSoftFloat32FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSqrt>(dlgSoftFloat32FPSqrt));
SetDelegateInfo(dlgSoftFloat32FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSub>(dlgSoftFloat32FPSub));
SetDelegateInfo(dlgSoftFloat32_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat32_16FPConvert>(dlgSoftFloat32_16FPConvert));
SetDelegateInfo(dlgSoftFloat64FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAdd>(dlgSoftFloat64FPAdd));
SetDelegateInfo(dlgSoftFloat64FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAddFpscr>(dlgSoftFloat64FPAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompare>(dlgSoftFloat64FPCompare));
SetDelegateInfo(dlgSoftFloat64FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQ>(dlgSoftFloat64FPCompareEQ));
SetDelegateInfo(dlgSoftFloat64FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQFpscr>(dlgSoftFloat64FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGE>(dlgSoftFloat64FPCompareGE));
SetDelegateInfo(dlgSoftFloat64FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGEFpscr>(dlgSoftFloat64FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGT>(dlgSoftFloat64FPCompareGT));
SetDelegateInfo(dlgSoftFloat64FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGTFpscr>(dlgSoftFloat64FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLE>(dlgSoftFloat64FPCompareLE));
SetDelegateInfo(dlgSoftFloat64FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLEFpscr>(dlgSoftFloat64FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLT>(dlgSoftFloat64FPCompareLT));
SetDelegateInfo(dlgSoftFloat64FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLTFpscr>(dlgSoftFloat64FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPDiv>(dlgSoftFloat64FPDiv));
SetDelegateInfo(dlgSoftFloat64FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMax>(dlgSoftFloat64FPMax));
SetDelegateInfo(dlgSoftFloat64FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxFpscr>(dlgSoftFloat64FPMaxFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNum>(dlgSoftFloat64FPMaxNum));
SetDelegateInfo(dlgSoftFloat64FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNumFpscr>(dlgSoftFloat64FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMin>(dlgSoftFloat64FPMin));
SetDelegateInfo(dlgSoftFloat64FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinFpscr>(dlgSoftFloat64FPMinFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNum>(dlgSoftFloat64FPMinNum));
SetDelegateInfo(dlgSoftFloat64FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNumFpscr>(dlgSoftFloat64FPMinNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMul>(dlgSoftFloat64FPMul));
SetDelegateInfo(dlgSoftFloat64FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulFpscr>(dlgSoftFloat64FPMulFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAdd>(dlgSoftFloat64FPMulAdd));
SetDelegateInfo(dlgSoftFloat64FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAddFpscr>(dlgSoftFloat64FPMulAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSub>(dlgSoftFloat64FPMulSub));
SetDelegateInfo(dlgSoftFloat64FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSubFpscr>(dlgSoftFloat64FPMulSubFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulX>(dlgSoftFloat64FPMulX));
SetDelegateInfo(dlgSoftFloat64FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulAdd>(dlgSoftFloat64FPNegMulAdd));
SetDelegateInfo(dlgSoftFloat64FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulSub>(dlgSoftFloat64FPNegMulSub));
SetDelegateInfo(dlgSoftFloat64FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimate>(dlgSoftFloat64FPRecipEstimate));
SetDelegateInfo(dlgSoftFloat64FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimateFpscr>(dlgSoftFloat64FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStep>(dlgSoftFloat64FPRecipStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStepFused>(dlgSoftFloat64FPRecipStepFused));
SetDelegateInfo(dlgSoftFloat64FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecpX>(dlgSoftFloat64FPRecpX));
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimate>(dlgSoftFloat64FPRSqrtEstimate));
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimateFpscr>(dlgSoftFloat64FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStep>(dlgSoftFloat64FPRSqrtStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStepFused>(dlgSoftFloat64FPRSqrtStepFused));
SetDelegateInfo(dlgSoftFloat64FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSqrt>(dlgSoftFloat64FPSqrt));
SetDelegateInfo(dlgSoftFloat64FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSub>(dlgSoftFloat64FPSub));
SetDelegateInfo(dlgSoftFloat64_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat64_16FPConvert>(dlgSoftFloat64_16FPConvert));
SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert));
}
private delegate double MathAbs(double value);

View file

@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 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 BackupDir = "1";
@ -857,14 +857,8 @@ namespace ARMeilleure.Translation.PTC
Stopwatch sw = Stopwatch.StartNew();
foreach (var thread in threads)
{
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
threads.ForEach((thread) => thread.Start());
threads.ForEach((thread) => thread.Join());
threads.Clear();

View file

@ -89,17 +89,6 @@ namespace ARMeilleure.Translation
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
{
if (cfg.Entry.Predecessors.Count != 0)
{
// We expect the entry block to have no predecessors.
// This is required because we have a implicit context load at the start of the function,
// but if there is a jump to the start of the function, the context load would trash the modified values.
// Here we insert a new entry block that will jump to the existing entry block.
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
cfg.UpdateEntry(newEntry);
}
// Compute local register inputs and outputs used inside blocks.
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
@ -212,7 +201,7 @@ namespace ARMeilleure.Translation
// The only block without any predecessor should be the entry block.
// It always needs a context load as it is the first block to run.
if (block == cfg.Entry || hasContextLoad)
if (block.Predecessors.Count == 0 || hasContextLoad)
{
long vecMask = globalInputs[block.Index].VecMask;
long intMask = globalInputs[block.Index].IntMask;

View file

@ -80,10 +80,7 @@ namespace ARMeilleure.Translation
return true;
}
if (!_disposed)
{
Monitor.Wait(Sync);
}
Monitor.Wait(Sync);
}
}

View file

@ -89,9 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2
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);

View file

@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
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);

View file

@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Backends.Common
private readonly object _lock = new();
private MemoryOwner<byte> _bufferOwner;
private IMemoryOwner<byte> _bufferOwner;
private Memory<byte> _buffer;
private int _size;
private int _headOffset;
@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Backends.Common
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
{
_bufferOwner = MemoryOwner<byte>.RentCleared(initialCapacity);
_bufferOwner = ByteMemoryPool.RentCleared(initialCapacity);
_buffer = _bufferOwner.Memory;
}
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Backends.Common
private void SetCapacityLocked(int capacity)
{
MemoryOwner<byte> newBufferOwner = MemoryOwner<byte>.RentCleared(capacity);
IMemoryOwner<byte> newBufferOwner = ByteMemoryPool.RentCleared(capacity);
Memory<byte> newBuffer = newBufferOwner.Memory;
if (_size > 0)

View file

@ -15,6 +15,7 @@ namespace Ryujinx.Audio.Renderer.Common
{
public const int Align = 0x10;
public const int BiquadStateOffset = 0x0;
public const int BiquadStateSize = 0x10;
/// <summary>
/// The state of the biquad filters of this voice.

View file

@ -16,15 +16,10 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="parameter">The biquad filter parameter</param>
/// <param name="state">The biquad filter state</param>
/// <param name="outputBuffer">The output buffer to write the result</param>
/// <param name="inputBuffer">The input buffer to 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProcessBiquadFilter(
ref BiquadFilterParameter parameter,
ref BiquadFilterState state,
Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer,
uint sampleCount)
public static void ProcessBiquadFilter(ref BiquadFilterParameter parameter, ref BiquadFilterState state, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
{
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
@ -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>
/// Apply multiple biquad filter.
/// </summary>
@ -142,15 +47,10 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="parameters">The biquad filter parameter</param>
/// <param name="states">The biquad filter state</param>
/// <param name="outputBuffer">The output buffer to write the result</param>
/// <param name="inputBuffer">The input buffer to 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProcessBiquadFilter(
ReadOnlySpan<BiquadFilterParameter> parameters,
Span<BiquadFilterState> states,
Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer,
uint sampleCount)
public static void ProcessBiquadFilter(ReadOnlySpan<BiquadFilterParameter> parameters, Span<BiquadFilterState> states, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
{
for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++)
{
@ -167,7 +67,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
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;
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;
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -30,10 +30,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
CopyMixBuffer,
LimiterVersion1,
LimiterVersion2,
MultiTapBiquadFilter,
GroupedBiquadFilter,
CaptureBuffer,
Compressor,
BiquadFilterAndMix,
MultiTapBiquadFilterAndMix,
}
}

View file

@ -1,11 +1,9 @@
using Ryujinx.Audio.Renderer.Dsp.Effect;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using Ryujinx.Audio.Renderer.Server.Effect;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Dsp.Command
{
@ -23,20 +21,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public CompressorParameter Parameter => _parameter;
public Memory<CompressorState> State { get; }
public Memory<EffectResultState> ResultState { get; }
public ushort[] OutputBufferIndices { get; }
public ushort[] InputBufferIndices { get; }
public bool IsEffectEnabled { get; }
private CompressorParameter _parameter;
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> resultState, bool isEnabled, int nodeId)
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
{
Enabled = true;
NodeId = nodeId;
_parameter = parameter;
State = state;
ResultState = resultState;
IsEffectEnabled = isEnabled;
@ -75,16 +71,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
if (IsEffectEnabled && _parameter.IsChannelCountValid())
{
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
{
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(ResultState.Span[0].SpecificData)[0];
statistics.Reset(_parameter.ChannelCount);
}
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
Span<float> channelInput = stackalloc float[_parameter.ChannelCount];
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
float unknown4 = state.Unknown4;
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
@ -103,8 +92,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
}
float mean = FloatingPointHelper.MeanSquare(channelInput);
float newMean = inputMovingAverage.Update(mean, _parameter.InputGain);
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
float z = 1.0f;
@ -123,7 +111,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
if (y >= state.Unknown14)
{
tmpGain = ((1.0f / _parameter.Ratio) - 1.0f) * (y - _parameter.Threshold);
tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
}
else
{
@ -138,7 +126,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
if ((unknown4 - z) <= 0.08f)
{
compressionEmaAlpha = _parameter.ReleaseCoefficient;
compressionEmaAlpha = Parameter.ReleaseCoefficient;
if ((unknown4 - z) >= -0.08f)
{
@ -152,31 +140,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
}
else
{
compressionEmaAlpha = _parameter.AttackCoefficient;
compressionEmaAlpha = Parameter.AttackCoefficient;
}
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
{
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
}
unknown4 = unknown4New;
previousCompressionEmaAlpha = compressionEmaAlpha;
if (!ResultState.IsEmpty)
{
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(ResultState.Span[0].SpecificData)[0];
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
{
statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f));
}
}
}
state.InputMovingAverage = inputMovingAverage;
@ -186,7 +161,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
}
else
{
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
if (InputBufferIndices[i] != OutputBufferIndices[i])
{

View file

@ -4,13 +4,13 @@ using System;
namespace Ryujinx.Audio.Renderer.Dsp.Command
{
public class MultiTapBiquadFilterCommand : ICommand
public class GroupedBiquadFilterCommand : ICommand
{
public bool Enabled { get; set; }
public int NodeId { get; }
public CommandType CommandType => CommandType.MultiTapBiquadFilter;
public CommandType CommandType => CommandType.GroupedBiquadFilter;
public uint EstimatedProcessingTime { get; set; }
@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private readonly int _outputBufferIndex;
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();
_biquadFilterStates = biquadFilterStateMemory;

View file

@ -38,10 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
}
}
@ -51,11 +51,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
if (IsEffectEnabled)
{
if (_parameter.Status == UsageState.Invalid)
if (Parameter.Status == UsageState.Invalid)
{
state = new LimiterState(ref _parameter, WorkBuffer);
}
else if (_parameter.Status == UsageState.New)
else if (Parameter.Status == UsageState.New)
{
LimiterState.UpdateParameter(ref _parameter);
}
@ -66,56 +66,56 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
{
Debug.Assert(_parameter.IsChannelCountValid());
Debug.Assert(Parameter.IsChannelCountValid());
if (IsEffectEnabled && _parameter.IsChannelCountValid())
if (IsEffectEnabled && Parameter.IsChannelCountValid())
{
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
}
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
{
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
{
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain;
float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain;
float sampleInputMax = Math.Abs(inputSample);
float inputCoefficient = _parameter.ReleaseCoefficient;
float inputCoefficient = Parameter.ReleaseCoefficient;
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
{
inputCoefficient = _parameter.AttackCoefficient;
inputCoefficient = Parameter.AttackCoefficient;
}
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
float attenuation = 1.0f;
if (detectorValue > _parameter.Threshold)
if (detectorValue > Parameter.Threshold)
{
attenuation = _parameter.Threshold / detectorValue;
attenuation = Parameter.Threshold / detectorValue;
}
float outputCoefficient = _parameter.ReleaseCoefficient;
float outputCoefficient = Parameter.ReleaseCoefficient;
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
{
outputCoefficient = _parameter.AttackCoefficient;
outputCoefficient = Parameter.AttackCoefficient;
}
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
float outputSample = delayedSample * compressionGain * _parameter.OutputGain;
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
@ -123,16 +123,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
state.DelayedSampleBufferPosition[channelIndex]++;
while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin)
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
{
state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin;
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
}
}
}
}
else
{
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
if (InputBufferIndices[i] != OutputBufferIndices[i])
{

View file

@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
}
}
@ -62,11 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
if (IsEffectEnabled)
{
if (_parameter.Status == UsageState.Invalid)
if (Parameter.Status == UsageState.Invalid)
{
state = new LimiterState(ref _parameter, WorkBuffer);
}
else if (_parameter.Status == UsageState.New)
else if (Parameter.Status == UsageState.New)
{
LimiterState.UpdateParameter(ref _parameter);
}
@ -77,63 +77,63 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
{
Debug.Assert(_parameter.IsChannelCountValid());
Debug.Assert(Parameter.IsChannelCountValid());
if (IsEffectEnabled && _parameter.IsChannelCountValid())
if (IsEffectEnabled && Parameter.IsChannelCountValid())
{
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
if (!ResultState.IsEmpty && Parameter.StatisticsReset)
{
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
statistics.Reset();
}
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
}
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
{
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
{
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain;
float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain;
float sampleInputMax = Math.Abs(inputSample);
float inputCoefficient = _parameter.ReleaseCoefficient;
float inputCoefficient = Parameter.ReleaseCoefficient;
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
{
inputCoefficient = _parameter.AttackCoefficient;
inputCoefficient = Parameter.AttackCoefficient;
}
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
float attenuation = 1.0f;
if (detectorValue > _parameter.Threshold)
if (detectorValue > Parameter.Threshold)
{
attenuation = _parameter.Threshold / detectorValue;
attenuation = Parameter.Threshold / detectorValue;
}
float outputCoefficient = _parameter.ReleaseCoefficient;
float outputCoefficient = Parameter.ReleaseCoefficient;
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
{
outputCoefficient = _parameter.AttackCoefficient;
outputCoefficient = Parameter.AttackCoefficient;
}
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
float outputSample = delayedSample * compressionGain * _parameter.OutputGain;
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
@ -141,9 +141,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
state.DelayedSampleBufferPosition[channelIndex]++;
while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin)
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
{
state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin;
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
}
if (!ResultState.IsEmpty)
@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
}
else
{
for (int i = 0; i < _parameter.ChannelCount; i++)
for (int i = 0; i < Parameter.ChannelCount; i++)
{
if (InputBufferIndices[i] != OutputBufferIndices[i])
{

View file

@ -24,14 +24,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public Memory<VoiceUpdateState> State { get; }
public MixRampGroupedCommand(
uint mixBufferCount,
uint inputBufferIndex,
uint outputBufferIndex,
ReadOnlySpan<float> volume0,
ReadOnlySpan<float> volume1,
Memory<VoiceUpdateState> state,
int nodeId)
public MixRampGroupedCommand(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> volume0, Span<float> volume1, Memory<VoiceUpdateState> state, int nodeId)
{
Enabled = true;
MixBufferCount = mixBufferCount;
@ -55,12 +48,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ProcessMixRampGrouped(
Span<float> outputBuffer,
ReadOnlySpan<float> inputBuffer,
float volume0,
float volume1,
int sampleCount)
private static float ProcessMixRampGrouped(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, float volume0, float volume1, int sampleCount)
{
float ramp = (volume1 - volume0) / sampleCount;
float volume = volume0;

View file

@ -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);
}
}
}
}

View file

@ -2,16 +2,12 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x20)]
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
public struct BiquadFilterState
{
public float State0;
public float State1;
public float State2;
public float State3;
public float State4;
public float State5;
public float State6;
public float State7;
}
}

View file

@ -90,16 +90,9 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
public bool MakeupGainEnabled;
/// <summary>
/// Indicate if the compressor effect should output statistics.
/// Reserved/padding.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool StatisticsEnabled;
/// <summary>
/// Indicate to the DSP that the user did a statistics reset.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool StatisticsReset;
private Array2<byte> _reserved;
/// <summary>
/// Check if the <see cref="ChannelCount"/> is valid.

View file

@ -1,38 +0,0 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
/// Effect result state for <seealso cref="Common.EffectType.Compressor"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CompressorStatistics
{
/// <summary>
/// Maximum input mean value since last reset.
/// </summary>
public float MaximumMean;
/// <summary>
/// Minimum output gain since last reset.
/// </summary>
public float MinimumGain;
/// <summary>
/// Last processed input sample, per channel.
/// </summary>
public Array6<float> LastSamples;
/// <summary>
/// Reset the statistics.
/// </summary>
/// <param name="channelCount">Number of channels to reset.</param>
public void Reset(ushort channelCount)
{
MaximumMean = 0.0f;
MinimumGain = 1.0f;
LastSamples.AsSpan()[..channelCount].Clear();
}
}
}

View file

@ -1,48 +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>
/// Set to true to force resetting the previous mix volumes.
/// </summary>
bool ResetPrevVolume { 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();
}
}

View file

@ -1,4 +1,3 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.Runtime.InteropServices;
@ -6,10 +5,10 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
/// Input header for a splitter destination version 1 update.
/// Input header for a splitter destination update.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SplitterDestinationInParameterVersion1 : ISplitterDestinationInParameter
public struct SplitterDestinationInParameter
{
/// <summary>
/// Magic of the input header.
@ -37,18 +36,12 @@ namespace Ryujinx.Audio.Renderer.Parameter
[MarshalAs(UnmanagedType.I1)]
public bool IsUsed;
/// <summary>
/// Set to true to force resetting the previous mix volumes.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool ResetPrevVolume;
/// <summary>
/// Reserved/padding.
/// </summary>
private unsafe fixed byte _reserved[2];
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 { }
/// <summary>
@ -57,15 +50,6 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mixBufferVolume);
readonly int ISplitterDestinationInParameter.Id => Id;
readonly int ISplitterDestinationInParameter.DestinationId => DestinationId;
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
/// <summary>
/// The expected constant of any input header.
/// </summary>

View file

@ -1,88 +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>
/// Set to true to force resetting the previous mix volumes.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool ResetPrevVolume;
/// <summary>
/// Reserved/padding.
/// </summary>
private unsafe fixed byte _reserved[10];
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
private struct MixArray { }
/// <summary>
/// Mix buffer volumes.
/// </summary>
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mixBufferVolume);
readonly int ISplitterDestinationInParameter.Id => Id;
readonly int ISplitterDestinationInParameter.DestinationId => DestinationId;
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
/// <summary>
/// The expected constant of any input header.
/// </summary>
private const uint ValidMagic = 0x44444E53;
/// <summary>
/// Check if the magic is valid.
/// </summary>
/// <returns>Returns true if the magic is valid.</returns>
public readonly bool IsMagicValid()
{
return Magic == ValidMagic;
}
}
}

View file

@ -1,7 +1,6 @@
using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.Command;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.Effect;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
@ -174,22 +173,6 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall;
}
Memory<BiquadFilterState> splitterBqfStates = Memory<BiquadFilterState>.Empty;
if (_behaviourContext.IsBiquadFilterParameterForSplitterEnabled() &&
parameter.SplitterCount > 0 &&
parameter.SplitterDestinationCount > 0)
{
splitterBqfStates = workBufferAllocator.Allocate<BiquadFilterState>(parameter.SplitterDestinationCount * SplitterContext.BqfStatesPerDestination, 0x10);
if (splitterBqfStates.IsEmpty)
{
return ResultCode.WorkBufferTooSmall;
}
splitterBqfStates.Span.Clear();
}
// Invalidate DSP cache on what was currently allocated with workBuffer.
AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
@ -309,7 +292,7 @@ namespace Ryujinx.Audio.Renderer.Server
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;
}
@ -792,13 +775,6 @@ namespace Ryujinx.Audio.Renderer.Server
// Splitter
size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter);
if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled() &&
parameter.SplitterCount > 0 &&
parameter.SplitterDestinationCount > 0)
{
size = WorkBufferAllocator.GetTargetSize<BiquadFilterState>(size, parameter.SplitterDestinationCount * SplitterContext.BqfStatesPerDestination, 0x10);
}
// DSP Voice
size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align);

View file

@ -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.
/// A new version of the command estimator was added to address timing changes caused by the voice changes.
/// Additionally, the rendering limit percent was incremented to 80%.
///
/// </summary>
/// <remarks>This was added in system update 6.0.0</remarks>
public const int Revision5 = 5 << 24;
@ -100,26 +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>
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>
/// REV13:
/// The compressor effect can now output statistics.
/// Splitter destinations now explicitly reset the previous mix volume, instead of doing so on first use.
/// </summary>
/// <remarks>This was added in system update 18.0.0</remarks>
public const int Revision13 = 13 << 24;
/// <summary>
/// Last revision supported by the implementation.
/// </summary>
public const int LastRevision = Revision13;
public const int LastRevision = Revision11;
/// <summary>
/// Target revision magic supported by the implementation.
@ -369,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.
/// </summary>
/// <returns>True if the audio renderer should use the optimization.</returns>
public bool UseMultiTapBiquadFilterProcessing()
public bool IsBiquadFilterGroupedOptimizationSupported()
{
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
}
@ -383,24 +368,6 @@ namespace Ryujinx.Audio.Renderer.Server
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision11);
}
/// <summary>
/// Check if the audio renderer should support biquad filter on splitter.
/// </summary>
/// <returns>True if the audio renderer support biquad filter on splitter</returns>
public bool IsBiquadFilterParameterForSplitterEnabled()
{
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12);
}
/// <summary>
/// Check if the audio renderer should support explicit previous mix volume reset on splitter.
/// </summary>
/// <returns>True if the audio renderer support explicit previous mix volume reset on splitter</returns>
public bool IsSplitterPrevVolumeResetSupported()
{
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13);
}
/// <summary>
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
/// </summary>

View file

@ -204,7 +204,7 @@ namespace Ryujinx.Audio.Renderer.Server
}
/// <summary>
/// Create a new <see cref="MultiTapBiquadFilterCommand"/>.
/// Create a new <see cref="GroupedBiquadFilterCommand"/>.
/// </summary>
/// <param name="baseIndex">The base index of the input and output buffer.</param>
/// <param name="filters">The biquad filter parameters.</param>
@ -213,9 +213,9 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="outputBufferOffset">The output buffer offset.</param>
/// <param name="isInitialized">Set to true if the biquad filter state is initialized.</param>
/// <param name="nodeId">The node id associated to this command.</param>
public void 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);
@ -232,7 +232,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="volume">The new volume.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
/// <param name="nodeId">The node id associated to this command.</param>
public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, 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);
@ -260,120 +260,6 @@ namespace Ryujinx.Audio.Renderer.Server
AddCommand(command);
}
/// <summary>
/// Generate a new <see cref="BiquadFilterAndMixCommand"/>.
/// </summary>
/// <param name="previousVolume">The previous volume.</param>
/// <param name="volume">The new volume.</param>
/// <param name="inputBufferIndex">The input buffer index.</param>
/// <param name="outputBufferIndex">The output buffer index.</param>
/// <param name="lastSampleIndex">The index in the <see cref="VoiceUpdateState.LastSamples"/> array to store the ramped sample.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
/// <param name="filter">The biquad filter parameter.</param>
/// <param name="biquadFilterState">The biquad state.</param>
/// <param name="previousBiquadFilterState">The previous biquad state.</param>
/// <param name="needInitialization">Set to true if the biquad filter state needs to be initialized.</param>
/// <param name="hasVolumeRamp">Set to true if the mix has volume ramp, and <paramref name="previousVolume"/> should be taken into account.</param>
/// <param name="isFirstMixBuffer">Set to true if the buffer is the first mix buffer.</param>
/// <param name="nodeId">The node id associated to this command.</param>
public void GenerateBiquadFilterAndMix(
float previousVolume,
float volume,
uint inputBufferIndex,
uint outputBufferIndex,
int lastSampleIndex,
Memory<VoiceUpdateState> state,
ref BiquadFilterParameter filter,
Memory<BiquadFilterState> biquadFilterState,
Memory<BiquadFilterState> previousBiquadFilterState,
bool needInitialization,
bool hasVolumeRamp,
bool isFirstMixBuffer,
int nodeId)
{
BiquadFilterAndMixCommand command = new(
previousVolume,
volume,
inputBufferIndex,
outputBufferIndex,
lastSampleIndex,
state,
ref filter,
biquadFilterState,
previousBiquadFilterState,
needInitialization,
hasVolumeRamp,
isFirstMixBuffer,
nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
AddCommand(command);
}
/// <summary>
/// Generate a new <see cref="MultiTapBiquadFilterAndMixCommand"/>.
/// </summary>
/// <param name="previousVolume">The previous volume.</param>
/// <param name="volume">The new volume.</param>
/// <param name="inputBufferIndex">The input buffer index.</param>
/// <param name="outputBufferIndex">The output buffer index.</param>
/// <param name="lastSampleIndex">The index in the <see cref="VoiceUpdateState.LastSamples"/> array to store the ramped sample.</param>
/// <param name="state">The <see cref="VoiceUpdateState"/> to generate the command from.</param>
/// <param name="filter0">First biquad filter parameter.</param>
/// <param name="filter1">Second biquad filter parameter.</param>
/// <param name="biquadFilterState0">First biquad state.</param>
/// <param name="biquadFilterState1">Second biquad state.</param>
/// <param name="previousBiquadFilterState0">First previous biquad state.</param>
/// <param name="previousBiquadFilterState1">Second previous biquad state.</param>
/// <param name="needInitialization0">Set to true if the first biquad filter state needs to be initialized.</param>
/// <param name="needInitialization1">Set to true if the second biquad filter state needs to be initialized.</param>
/// <param name="hasVolumeRamp">Set to true if the mix has volume ramp, and <paramref name="previousVolume"/> should be taken into account.</param>
/// <param name="isFirstMixBuffer">Set to true if the buffer is the first mix buffer.</param>
/// <param name="nodeId">The node id associated to this command.</param>
public void GenerateMultiTapBiquadFilterAndMix(
float previousVolume,
float volume,
uint inputBufferIndex,
uint outputBufferIndex,
int lastSampleIndex,
Memory<VoiceUpdateState> state,
ref BiquadFilterParameter filter0,
ref BiquadFilterParameter filter1,
Memory<BiquadFilterState> biquadFilterState0,
Memory<BiquadFilterState> biquadFilterState1,
Memory<BiquadFilterState> previousBiquadFilterState0,
Memory<BiquadFilterState> previousBiquadFilterState1,
bool needInitialization0,
bool needInitialization1,
bool hasVolumeRamp,
bool isFirstMixBuffer,
int nodeId)
{
MultiTapBiquadFilterAndMixCommand command = new(
previousVolume,
volume,
inputBufferIndex,
outputBufferIndex,
lastSampleIndex,
state,
ref filter0,
ref filter1,
biquadFilterState0,
biquadFilterState1,
previousBiquadFilterState0,
previousBiquadFilterState1,
needInitialization0,
needInitialization1,
hasVolumeRamp,
isFirstMixBuffer,
nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
AddCommand(command);
}
/// <summary>
/// Generate a new <see cref="DepopForMixBuffersCommand"/>.
/// </summary>
@ -382,7 +268,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="bufferCount">The buffer count.</param>
/// <param name="nodeId">The node id associated to this command.</param>
/// <param name="sampleRate">The target sample rate in use.</param>
public void 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);
@ -583,20 +469,11 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
/// <summary>
/// Generate a new <see cref="CompressorCommand"/>.
/// </summary>
/// <param name="bufferOffset">The target buffer offset.</param>
/// <param name="parameter">The compressor parameter.</param>
/// <param name="state">The compressor state.</param>
/// <param name="effectResultState">The DSP effect result state.</param>
/// <param name="isEnabled">Set to true if the effect should be active.</param>
/// <param name="nodeId">The node id associated to this command.</param>
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> effectResultState, bool isEnabled, int nodeId)
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
{
if (parameter.IsChannelCountValid())
{
CompressorCommand command = new(bufferOffset, parameter, state, effectResultState, isEnabled, nodeId);
CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);

View file

@ -12,7 +12,6 @@ using Ryujinx.Audio.Renderer.Server.Voice;
using Ryujinx.Audio.Renderer.Utils;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Server
{
@ -47,13 +46,12 @@ namespace Ryujinx.Audio.Renderer.Server
{
ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
_commandBuffer.GenerateDepopPrepare(
dspState,
_rendererContext.DepopBuffer,
mix.BufferCount,
mix.BufferOffset,
voiceState.NodeId,
voiceState.WasPlaying);
_commandBuffer.GenerateDepopPrepare(dspState,
_rendererContext.DepopBuffer,
mix.BufferCount,
mix.BufferOffset,
voiceState.NodeId,
voiceState.WasPlaying);
}
else if (voiceState.SplitterId != Constants.UnusedSplitterId)
{
@ -61,13 +59,15 @@ namespace Ryujinx.Audio.Renderer.Server
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;
}
ref SplitterDestination destination = ref destinationSpan[0];
if (destination.IsConfigured())
{
int mixId = destination.DestinationId;
@ -76,13 +76,12 @@ namespace Ryujinx.Audio.Renderer.Server
{
ref MixState mix = ref _mixContext.GetState(mixId);
_commandBuffer.GenerateDepopPrepare(
dspState,
_rendererContext.DepopBuffer,
mix.BufferCount,
mix.BufferOffset,
voiceState.NodeId,
voiceState.WasPlaying);
_commandBuffer.GenerateDepopPrepare(dspState,
_rendererContext.DepopBuffer,
mix.BufferCount,
mix.BufferOffset,
voiceState.NodeId,
voiceState.WasPlaying);
destination.MarkAsNeedToUpdateInternalState();
}
@ -96,39 +95,35 @@ namespace Ryujinx.Audio.Renderer.Server
if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported())
{
_commandBuffer.GenerateDataSourceVersion2(
ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex,
voiceState.NodeId);
_commandBuffer.GenerateDataSourceVersion2(ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex,
voiceState.NodeId);
}
else
{
switch (voiceState.SampleFormat)
{
case SampleFormat.PcmInt16:
_commandBuffer.GeneratePcmInt16DataSourceVersion1(
ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex,
voiceState.NodeId);
_commandBuffer.GeneratePcmInt16DataSourceVersion1(ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex,
voiceState.NodeId);
break;
case SampleFormat.PcmFloat:
_commandBuffer.GeneratePcmFloatDataSourceVersion1(
ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex,
voiceState.NodeId);
_commandBuffer.GeneratePcmFloatDataSourceVersion1(ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
(ushort)channelIndex,
voiceState.NodeId);
break;
case SampleFormat.Adpcm:
_commandBuffer.GenerateAdpcmDataSourceVersion1(
ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
voiceState.NodeId);
_commandBuffer.GenerateAdpcmDataSourceVersion1(ref voiceState,
dspState,
(ushort)_rendererContext.MixBufferCount,
voiceState.NodeId);
break;
default:
throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}");
@ -139,14 +134,14 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
{
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
bool supportsOptimizedPath = _rendererContext.BehaviourContext.IsBiquadFilterGroupedOptimizationSupported();
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);
_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
{
@ -156,134 +151,33 @@ namespace Ryujinx.Audio.Renderer.Server
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);
_commandBuffer.GenerateBiquadFilter(
baseIndex,
ref filter,
stateMemory.Slice(i, 1),
bufferOffset,
bufferOffset,
!voiceState.BiquadFilterNeedInitialization[i],
nodeId);
_commandBuffer.GenerateBiquadFilter(baseIndex,
ref filter,
stateMemory.Slice(i, 1),
bufferOffset,
bufferOffset,
!voiceState.BiquadFilterNeedInitialization[i],
nodeId);
}
}
}
}
private void GenerateVoiceMixWithSplitter(
SplitterDestination destination,
Memory<VoiceUpdateState> state,
uint bufferOffset,
uint bufferCount,
uint bufferIndex,
int nodeId)
{
ReadOnlySpan<float> mixVolumes = destination.MixBufferVolume;
ReadOnlySpan<float> previousMixVolumes = destination.PreviousMixBufferVolume;
ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0);
ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1);
Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
bool isFirstMixBuffer = true;
for (int i = 0; i < bufferCount; i++)
{
float previousMixVolume = previousMixVolumes[i];
float mixVolume = mixVolumes[i];
if (mixVolume != 0.0f || previousMixVolume != 0.0f)
{
if (bqf0.Enable && bqf1.Enable)
{
_commandBuffer.GenerateMultiTapBiquadFilterAndMix(
previousMixVolume,
mixVolume,
bufferIndex,
bufferOffset + (uint)i,
i,
state,
ref bqf0,
ref bqf1,
bqfState[..1],
bqfState.Slice(1, 1),
bqfState.Slice(2, 1),
bqfState.Slice(3, 1),
!destination.IsBiquadFilterEnabledPrev(),
!destination.IsBiquadFilterEnabledPrev(),
true,
isFirstMixBuffer,
nodeId);
destination.UpdateBiquadFilterEnabledPrev(0);
destination.UpdateBiquadFilterEnabledPrev(1);
}
else if (bqf0.Enable)
{
_commandBuffer.GenerateBiquadFilterAndMix(
previousMixVolume,
mixVolume,
bufferIndex,
bufferOffset + (uint)i,
i,
state,
ref bqf0,
bqfState[..1],
bqfState.Slice(1, 1),
!destination.IsBiquadFilterEnabledPrev(),
true,
isFirstMixBuffer,
nodeId);
destination.UpdateBiquadFilterEnabledPrev(0);
}
else if (bqf1.Enable)
{
_commandBuffer.GenerateBiquadFilterAndMix(
previousMixVolume,
mixVolume,
bufferIndex,
bufferOffset + (uint)i,
i,
state,
ref bqf1,
bqfState[..1],
bqfState.Slice(1, 1),
!destination.IsBiquadFilterEnabledPrev(),
true,
isFirstMixBuffer,
nodeId);
destination.UpdateBiquadFilterEnabledPrev(1);
}
isFirstMixBuffer = false;
}
}
}
private void GenerateVoiceMix(
ReadOnlySpan<float> mixVolumes,
ReadOnlySpan<float> previousMixVolumes,
Memory<VoiceUpdateState> state,
uint bufferOffset,
uint bufferCount,
uint bufferIndex,
int nodeId)
private void GenerateVoiceMix(Span<float> mixVolumes, Span<float> previousMixVolumes, Memory<VoiceUpdateState> state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId)
{
if (bufferCount > Constants.VoiceChannelCountMax)
{
_commandBuffer.GenerateMixRampGrouped(
bufferCount,
bufferIndex,
bufferOffset,
previousMixVolumes,
mixVolumes,
state,
nodeId);
_commandBuffer.GenerateMixRampGrouped(bufferCount,
bufferIndex,
bufferOffset,
previousMixVolumes,
mixVolumes,
state,
nodeId);
}
else
{
@ -294,14 +188,13 @@ namespace Ryujinx.Audio.Renderer.Server
if (mixVolume != 0.0f || previousMixVolume != 0.0f)
{
_commandBuffer.GenerateMixRamp(
previousMixVolume,
mixVolume,
bufferIndex,
bufferOffset + (uint)i,
i,
state,
nodeId);
_commandBuffer.GenerateMixRamp(previousMixVolume,
mixVolume,
bufferIndex,
bufferOffset + (uint)i,
i,
state,
nodeId);
}
}
}
@ -378,11 +271,10 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
}
_commandBuffer.GenerateVolumeRamp(
voiceState.PreviousVolume,
voiceState.Volume,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
_commandBuffer.GenerateVolumeRamp(voiceState.PreviousVolume,
voiceState.Volume,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
if (performanceInitialized)
{
@ -399,13 +291,15 @@ namespace Ryujinx.Audio.Renderer.Server
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;
}
ref SplitterDestination destination = ref destinationSpan[0];
destinationId += (int)channelsCount;
if (destination.IsConfigured())
@ -416,27 +310,13 @@ namespace Ryujinx.Audio.Renderer.Server
{
ref MixState mix = ref _mixContext.GetState(mixId);
if (destination.IsBiquadFilterEnabled())
{
GenerateVoiceMixWithSplitter(
destination,
dspStateMemory,
mix.BufferOffset,
mix.BufferCount,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
}
else
{
GenerateVoiceMix(
destination.MixBufferVolume,
destination.PreviousMixBufferVolume,
dspStateMemory,
mix.BufferOffset,
mix.BufferCount,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
}
GenerateVoiceMix(destination.MixBufferVolume,
destination.PreviousMixBufferVolume,
dspStateMemory,
mix.BufferOffset,
mix.BufferCount,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
destination.MarkAsNeedToUpdateInternalState();
}
@ -457,14 +337,13 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
}
GenerateVoiceMix(
channelResource.Mix.AsSpan(),
channelResource.PreviousMix.AsSpan(),
dspStateMemory,
mix.BufferOffset,
mix.BufferCount,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
GenerateVoiceMix(channelResource.Mix.AsSpan(),
channelResource.PreviousMix.AsSpan(),
dspStateMemory,
mix.BufferOffset,
mix.BufferCount,
_rendererContext.MixBufferCount + (uint)channelIndex,
nodeId);
if (performanceInitialized)
{
@ -530,11 +409,10 @@ namespace Ryujinx.Audio.Renderer.Server
{
if (effect.Parameter.Volumes[i] != 0.0f)
{
_commandBuffer.GenerateMix(
(uint)bufferOffset + effect.Parameter.Input[i],
(uint)bufferOffset + effect.Parameter.Output[i],
nodeId,
effect.Parameter.Volumes[i]);
_commandBuffer.GenerateMix((uint)bufferOffset + effect.Parameter.Input[i],
(uint)bufferOffset + effect.Parameter.Output[i],
nodeId,
effect.Parameter.Volumes[i]);
}
}
}
@ -569,18 +447,17 @@ namespace Ryujinx.Audio.Renderer.Server
updateCount = newUpdateCount;
}
_commandBuffer.GenerateAuxEffect(
bufferOffset,
effect.Parameter.Input[i],
effect.Parameter.Output[i],
ref effect.State,
effect.IsEnabled,
effect.Parameter.BufferStorageSize,
effect.State.SendBufferInfoBase,
effect.State.ReturnBufferInfoBase,
updateCount,
writeOffset,
nodeId);
_commandBuffer.GenerateAuxEffect(bufferOffset,
effect.Parameter.Input[i],
effect.Parameter.Output[i],
ref effect.State,
effect.IsEnabled,
effect.Parameter.BufferStorageSize,
effect.State.SendBufferInfoBase,
effect.State.ReturnBufferInfoBase,
updateCount,
writeOffset,
nodeId);
writeOffset = newUpdateCount;
@ -623,7 +500,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (effect.IsEnabled)
{
bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
(effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
BiquadFilterParameter parameter = new()
{
@ -635,14 +512,11 @@ namespace Ryujinx.Audio.Renderer.Server
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
{
_commandBuffer.GenerateBiquadFilter(
(int)bufferOffset,
ref parameter,
effect.State.Slice(i, 1),
effect.Parameter.Input[i],
effect.Parameter.Output[i],
needInitialization,
nodeId);
_commandBuffer.GenerateBiquadFilter((int)bufferOffset, ref parameter, effect.State.Slice(i, 1),
effect.Parameter.Input[i],
effect.Parameter.Output[i],
needInitialization,
nodeId);
}
}
else
@ -717,16 +591,15 @@ namespace Ryujinx.Audio.Renderer.Server
updateCount = newUpdateCount;
}
_commandBuffer.GenerateCaptureEffect(
bufferOffset,
effect.Parameter.Input[i],
effect.State.SendBufferInfo,
effect.IsEnabled,
effect.Parameter.BufferStorageSize,
effect.State.SendBufferInfoBase,
updateCount,
writeOffset,
nodeId);
_commandBuffer.GenerateCaptureEffect(bufferOffset,
effect.Parameter.Input[i],
effect.State.SendBufferInfo,
effect.IsEnabled,
effect.Parameter.BufferStorageSize,
effect.State.SendBufferInfoBase,
updateCount,
writeOffset,
nodeId);
writeOffset = newUpdateCount;
@ -735,28 +608,15 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId)
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId)
{
Debug.Assert(effect.Type == EffectType.Compressor);
Memory<EffectResultState> dspResultState;
if (effect.Parameter.StatisticsEnabled)
{
dspResultState = _effectContext.GetDspStateMemory(effectId);
}
else
{
dspResultState = Memory<EffectResultState>.Empty;
}
_commandBuffer.GenerateCompressorEffect(
bufferOffset,
effect.Parameter,
effect.State,
dspResultState,
effect.IsEnabled,
nodeId);
_commandBuffer.GenerateCompressorEffect(bufferOffset,
effect.Parameter,
effect.State,
effect.IsEnabled,
nodeId);
}
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
@ -769,11 +629,8 @@ namespace Ryujinx.Audio.Renderer.Server
bool performanceInitialized = false;
if (_performanceManager != null && _performanceManager.GetNextEntry(
out performanceEntry,
effect.GetPerformanceDetailType(),
isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix,
nodeId))
if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(),
isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId))
{
performanceInitialized = true;
@ -807,7 +664,7 @@ namespace Ryujinx.Audio.Renderer.Server
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
break;
case EffectType.Compressor:
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId);
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId);
break;
default:
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
@ -849,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)
{
if (mix.HasAnyDestination())
@ -944,13 +722,15 @@ namespace Ryujinx.Audio.Renderer.Server
{
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;
}
ref SplitterDestination destination = ref destinationSpan[0];
if (destination.IsConfigured())
{
int mixId = destination.DestinationId;
@ -961,32 +741,16 @@ namespace Ryujinx.Audio.Renderer.Server
uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
bool isFirstMixBuffer = true;
for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
{
float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
if (volume != 0.0f)
{
if (destination.IsBiquadFilterEnabled())
{
GenerateMixWithSplitter(
inputBufferIndex,
destinationMix.BufferOffset + bufferDestinationIndex,
volume,
destination,
ref isFirstMixBuffer,
mix.NodeId);
}
else
{
_commandBuffer.GenerateMix(
inputBufferIndex,
destinationMix.BufferOffset + bufferDestinationIndex,
mix.NodeId,
volume);
}
_commandBuffer.GenerateMix(inputBufferIndex,
destinationMix.BufferOffset + bufferDestinationIndex,
mix.NodeId,
volume);
}
}
}
@ -1006,11 +770,10 @@ namespace Ryujinx.Audio.Renderer.Server
if (volume != 0.0f)
{
_commandBuffer.GenerateMix(
mix.BufferOffset + bufferIndex,
destinationMix.BufferOffset + bufferDestinationIndex,
mix.NodeId,
volume);
_commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex,
destinationMix.BufferOffset + bufferDestinationIndex,
mix.NodeId,
volume);
}
}
}
@ -1020,12 +783,11 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateSubMix(ref MixState subMix)
{
_commandBuffer.GenerateDepopForMixBuffers(
_rendererContext.DepopBuffer,
subMix.BufferOffset,
subMix.BufferCount,
subMix.NodeId,
subMix.SampleRate);
_commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
subMix.BufferOffset,
subMix.BufferCount,
subMix.NodeId,
subMix.SampleRate);
GenerateEffects(ref subMix);
@ -1085,12 +847,11 @@ namespace Ryujinx.Audio.Renderer.Server
{
ref MixState finalMix = ref _mixContext.GetFinalState();
_commandBuffer.GenerateDepopForMixBuffers(
_rendererContext.DepopBuffer,
finalMix.BufferOffset,
finalMix.BufferCount,
finalMix.NodeId,
finalMix.SampleRate);
_commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
finalMix.BufferOffset,
finalMix.BufferCount,
finalMix.NodeId,
finalMix.SampleRate);
GenerateEffects(ref finalMix);
@ -1121,10 +882,9 @@ namespace Ryujinx.Audio.Renderer.Server
GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
}
_commandBuffer.GenerateVolume(
finalMix.Volume,
finalMix.BufferOffset + bufferIndex,
nodeId);
_commandBuffer.GenerateVolume(finalMix.Volume,
finalMix.BufferOffset + bufferIndex,
nodeId);
if (performanceSubInitialized)
{
@ -1178,45 +938,41 @@ namespace Ryujinx.Audio.Renderer.Server
if (useCustomDownMixingCommand)
{
_commandBuffer.GenerateDownMixSurroundToStereo(
finalMix.BufferOffset,
sink.Parameter.Input.AsSpan(),
sink.Parameter.Input.AsSpan(),
sink.DownMixCoefficients,
Constants.InvalidNodeId);
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
sink.Parameter.Input.AsSpan(),
sink.Parameter.Input.AsSpan(),
sink.DownMixCoefficients,
Constants.InvalidNodeId);
}
// NOTE: We do the downmixing at the DSP level as it's easier that way.
else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
{
_commandBuffer.GenerateDownMixSurroundToStereo(
finalMix.BufferOffset,
sink.Parameter.Input.AsSpan(),
sink.Parameter.Input.AsSpan(),
Constants.DefaultSurroundToStereoCoefficients,
Constants.InvalidNodeId);
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
sink.Parameter.Input.AsSpan(),
sink.Parameter.Input.AsSpan(),
Constants.DefaultSurroundToStereoCoefficients,
Constants.InvalidNodeId);
}
CommandList commandList = _commandBuffer.CommandList;
if (sink.UpsamplerState != null)
{
_commandBuffer.GenerateUpsample(
finalMix.BufferOffset,
sink.UpsamplerState,
sink.Parameter.InputCount,
sink.Parameter.Input.AsSpan(),
commandList.BufferCount,
commandList.SampleCount,
commandList.SampleRate,
Constants.InvalidNodeId);
_commandBuffer.GenerateUpsample(finalMix.BufferOffset,
sink.UpsamplerState,
sink.Parameter.InputCount,
sink.Parameter.Input.AsSpan(),
commandList.BufferCount,
commandList.SampleCount,
commandList.SampleRate,
Constants.InvalidNodeId);
}
_commandBuffer.GenerateDeviceSink(
finalMix.BufferOffset,
sink,
_rendererContext.SessionId,
commandList.Buffers,
Constants.InvalidNodeId);
_commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
sink,
_rendererContext.SessionId,
commandList.Buffers,
Constants.InvalidNodeId);
}
private void GenerateSink(BaseSink sink, ref MixState finalMix)

View file

@ -170,7 +170,7 @@ namespace Ryujinx.Audio.Renderer.Server
return 0;
}
public uint Estimate(MultiTapBiquadFilterCommand command)
public uint Estimate(GroupedBiquadFilterCommand command)
{
return 0;
}
@ -184,15 +184,5 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
public uint Estimate(BiquadFilterAndMixCommand command)
{
return 0;
}
public uint Estimate(MultiTapBiquadFilterAndMixCommand command)
{
return 0;
}
}
}

View file

@ -462,7 +462,7 @@ namespace Ryujinx.Audio.Renderer.Server
return 0;
}
public uint Estimate(MultiTapBiquadFilterCommand command)
public uint Estimate(GroupedBiquadFilterCommand command)
{
return 0;
}
@ -476,15 +476,5 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
public uint Estimate(BiquadFilterAndMixCommand command)
{
return 0;
}
public uint Estimate(MultiTapBiquadFilterAndMixCommand command)
{
return 0;
}
}
}

View file

@ -632,7 +632,7 @@ namespace Ryujinx.Audio.Renderer.Server
};
}
public virtual uint Estimate(MultiTapBiquadFilterCommand command)
public virtual uint Estimate(GroupedBiquadFilterCommand command)
{
return 0;
}
@ -646,15 +646,5 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
public virtual uint Estimate(BiquadFilterAndMixCommand command)
{
return 0;
}
public virtual uint Estimate(MultiTapBiquadFilterAndMixCommand command)
{
return 0;
}
}
}

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
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);

View file

@ -169,28 +169,14 @@ namespace Ryujinx.Audio.Renderer.Server
{
if (command.Enabled)
{
if (command.Parameter.StatisticsEnabled)
return command.Parameter.ChannelCount switch
{
return command.Parameter.ChannelCount switch
{
1 => 22100,
2 => 33211,
4 => 41587,
6 => 58819,
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
else
{
return command.Parameter.ChannelCount switch
{
1 => 19052,
2 => 29852,
4 => 37904,
6 => 55020,
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
1 => 34431,
2 => 44253,
4 => 63827,
6 => 83361,
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
return command.Parameter.ChannelCount switch
@ -205,28 +191,14 @@ namespace Ryujinx.Audio.Renderer.Server
if (command.Enabled)
{
if (command.Parameter.StatisticsEnabled)
return command.Parameter.ChannelCount switch
{
return command.Parameter.ChannelCount switch
{
1 => 32518,
2 => 49102,
4 => 61685,
6 => 87250,
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
else
{
return command.Parameter.ChannelCount switch
{
1 => 27963,
2 => 44016,
4 => 56183,
6 => 81862,
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
1 => 51095,
2 => 65693,
4 => 95383,
6 => 124510,
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
return command.Parameter.ChannelCount switch
@ -238,53 +210,5 @@ namespace Ryujinx.Audio.Renderer.Server
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
};
}
public override uint Estimate(BiquadFilterAndMixCommand command)
{
Debug.Assert(SampleCount == 160 || SampleCount == 240);
if (command.HasVolumeRamp)
{
if (SampleCount == 160)
{
return 5204;
}
return 6683;
}
else
{
if (SampleCount == 160)
{
return 3427;
}
return 4752;
}
}
public override uint Estimate(MultiTapBiquadFilterAndMixCommand command)
{
Debug.Assert(SampleCount == 160 || SampleCount == 240);
if (command.HasVolumeRamp)
{
if (SampleCount == 160)
{
return 7939;
}
return 10669;
}
else
{
if (SampleCount == 160)
{
return 6256;
}
return 8683;
}
}
}
}

View file

@ -62,19 +62,6 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
UpdateUsageStateForCommandGeneration();
Parameter.Status = UsageState.Enabled;
Parameter.StatisticsReset = false;
}
public override void InitializeResultState(ref EffectResultState state)
{
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(state.SpecificData)[0];
statistics.Reset(Parameter.ChannelCount);
}
public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState)
{
destState = srcState;
}
}
}

View file

@ -33,10 +33,8 @@ namespace Ryujinx.Audio.Renderer.Server
uint Estimate(UpsampleCommand command);
uint Estimate(LimiterCommandVersion1 command);
uint Estimate(LimiterCommandVersion2 command);
uint Estimate(MultiTapBiquadFilterCommand command);
uint Estimate(GroupedBiquadFilterCommand command);
uint Estimate(CaptureBufferCommand command);
uint Estimate(CompressorCommand command);
uint Estimate(BiquadFilterAndMixCommand command);
uint Estimate(MultiTapBiquadFilterAndMixCommand command);
}
}

View file

@ -225,11 +225,11 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
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)
{

View file

@ -18,12 +18,16 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
if (version == 2)
{
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2,
PerformanceEntryVersion2,
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
}
if (version == 1)
{
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1,
PerformanceEntryVersion1,
PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
}
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");

View file

@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
{
performanceEntry = null;
if (_entryDetailIndex >= MaxFrameDetailCount)
if (_entryDetailIndex > MaxFrameDetailCount)
{
return false;
}
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
};
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex);
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];

View file

@ -1,5 +1,4 @@
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Utils;
using Ryujinx.Common;
@ -16,63 +15,33 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public class SplitterContext
{
/// <summary>
/// Amount of biquad filter states per splitter destination.
/// </summary>
public const int BqfStatesPerDestination = 4;
/// <summary>
/// Storage for <see cref="SplitterState"/>.
/// </summary>
private Memory<SplitterState> _splitters;
/// <summary>
/// Storage for <see cref="SplitterDestinationVersion1"/>.
/// Storage for <see cref="SplitterDestination"/>.
/// </summary>
private Memory<SplitterDestinationVersion1> _splitterDestinationsV1;
/// <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; }
private Memory<SplitterDestination> _splitterDestinations;
/// <summary>
/// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.
/// </summary>
public bool IsBugFixed { get; private set; }
/// <summary>
/// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use.
/// </summary>
public bool IsSplitterPrevVolumeResetSupported { get; private set; }
/// <summary>
/// Initialize <see cref="SplitterContext"/>.
/// </summary>
/// <param name="behaviourContext">The behaviour context.</param>
/// <param name="parameter">The audio renderer configuration.</param>
/// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param>
/// <param name="splitterBqfStates">Memory to store the biquad filtering state for splitters during processing.</param>
/// <returns>Return true if the initialization was successful.</returns>
public bool Initialize(
ref BehaviourContext behaviourContext,
ref AudioRendererConfiguration parameter,
WorkBufferAllocator workBufferAllocator,
Memory<BiquadFilterState> splitterBqfStates)
public bool Initialize(ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator)
{
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;
}
@ -91,64 +60,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
splitter = new SplitterState(splitterId++);
}
Memory<SplitterDestinationVersion1> splitterDestinationsV1 = Memory<SplitterDestinationVersion1>.Empty;
Memory<SplitterDestinationVersion2> splitterDestinationsV2 = Memory<SplitterDestinationVersion2>.Empty;
Memory<SplitterDestination> splitterDestinations = workBufferAllocator.Allocate<SplitterDestination>(parameter.SplitterDestinationCount,
SplitterDestination.Alignment);
if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled())
if (splitterDestinations.IsEmpty)
{
Version = 1;
splitterDestinationsV1 = workBufferAllocator.Allocate<SplitterDestinationVersion1>(parameter.SplitterDestinationCount,
SplitterDestinationVersion1.Alignment);
if (splitterDestinationsV1.IsEmpty)
{
return false;
}
int splitterDestinationId = 0;
foreach (ref SplitterDestinationVersion1 data in splitterDestinationsV1.Span)
{
data = new SplitterDestinationVersion1(splitterDestinationId++);
}
}
else
{
Version = 2;
splitterDestinationsV2 = workBufferAllocator.Allocate<SplitterDestinationVersion2>(parameter.SplitterDestinationCount,
SplitterDestinationVersion2.Alignment);
if (splitterDestinationsV2.IsEmpty)
{
return false;
}
int splitterDestinationId = 0;
foreach (ref SplitterDestinationVersion2 data in splitterDestinationsV2.Span)
{
data = new SplitterDestinationVersion2(splitterDestinationId++);
}
if (parameter.SplitterDestinationCount > 0)
{
// Official code stores it in the SplitterDestinationVersion2 struct,
// but we don't to avoid using unsafe code.
splitterBqfStates.Span.Clear();
_splitterBqfStates = splitterBqfStates;
}
else
{
_splitterBqfStates = Memory<BiquadFilterState>.Empty;
}
return false;
}
IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported();
int splitterDestinationId = 0;
foreach (ref SplitterDestination data in splitterDestinations.Span)
{
data = new SplitterDestination(splitterDestinationId++);
}
SplitterState.InitializeSplitters(splitters.Span);
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
Setup(splitters, splitterDestinations, behaviourContext.IsSplitterBugFixed());
return true;
}
@ -165,15 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
if (behaviourContext.IsSplitterSupported())
{
size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment);
if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled())
{
size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion2>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment);
}
else
{
size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion1>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment);
}
size = WorkBufferAllocator.GetTargetSize<SplitterDestination>(size, parameter.SplitterDestinationCount, SplitterDestination.Alignment);
if (behaviourContext.IsSplitterBugFixed())
{
@ -190,18 +110,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// Setup the <see cref="SplitterContext"/> instance.
/// </summary>
/// <param name="splitters">The <see cref="SplitterState"/> storage.</param>
/// <param name="splitterDestinationsV1">The <see cref="SplitterDestinationVersion1"/> storage.</param>
/// <param name="splitterDestinationsV2">The <see cref="SplitterDestinationVersion2"/> storage.</param>
/// <param name="splitterDestinations">The <see cref="SplitterDestination"/> storage.</param>
/// <param name="isBugFixed">If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.</param>
private void Setup(
Memory<SplitterState> splitters,
Memory<SplitterDestinationVersion1> splitterDestinationsV1,
Memory<SplitterDestinationVersion2> splitterDestinationsV2,
bool isBugFixed)
private void Setup(Memory<SplitterState> splitters, Memory<SplitterDestination> splitterDestinations, bool isBugFixed)
{
_splitters = splitters;
_splitterDestinationsV1 = splitterDestinationsV1;
_splitterDestinationsV2 = splitterDestinationsV2;
_splitterDestinations = splitterDestinations;
IsBugFixed = isBugFixed;
}
@ -227,9 +141,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
return 0;
}
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length;
return length / _splitters.Length;
return _splitterDestinations.Length / _splitters.Length;
}
/// <summary>
@ -266,39 +178,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
}
/// <summary>
/// Update one splitter destination data from user parameters.
/// </summary>
/// <param name="input">The raw data after the splitter header.</param>
/// <returns>True if the update was successful, false otherwise</returns>
private bool UpdateData<T>(ref SequenceReader<byte> input) where T : unmanaged, ISplitterDestinationInParameter
{
ref readonly T parameter = ref input.GetRefOrRefToCopy<T>(out _);
Debug.Assert(parameter.IsMagicValid());
if (parameter.IsMagicValid())
{
int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length;
if (parameter.Id >= 0 && parameter.Id < length)
{
SplitterDestination destination = GetDestination(parameter.Id);
destination.Update(parameter, IsSplitterPrevVolumeResetSupported);
}
return true;
}
else
{
input.Rewind(Unsafe.SizeOf<T>());
return false;
}
}
/// <summary>
/// Update one or multiple splitter destination data from user parameters.
/// Update one or multiple <see cref="SplitterDestination"/> from user parameters.
/// </summary>
/// <param name="inputHeader">The splitter header.</param>
/// <param name="input">The raw data after the splitter header.</param>
@ -306,23 +186,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
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;
}
}
else if (Version == 2)
{
if (!UpdateData<SplitterDestinationInParameterVersion2>(ref input))
{
break;
ref SplitterDestination destination = ref GetDestination(parameter.Id);
destination.Update(parameter);
}
}
else
{
Debug.Fail($"Invalid splitter context version {Version}.");
input.Rewind(Unsafe.SizeOf<SplitterDestinationInParameter>());
break;
}
}
}
@ -334,7 +214,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>Return true if the update was successful.</returns>
public bool Update(ref SequenceReader<byte> input)
{
if (!UsingSplitter())
if (_splitterDestinations.IsEmpty || _splitters.IsEmpty)
{
return true;
}
@ -371,52 +251,45 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
}
/// <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>
/// <param name="id">The index to use.</param>
/// <returns>A reference to the splitter destination data at the given <paramref name="id"/>.</returns>
public SplitterDestination GetDestination(int id)
/// <returns>A reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.</returns>
public ref SplitterDestination GetDestination(int id)
{
if (_splitterDestinationsV2.IsEmpty)
{
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length));
}
else
{
return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length));
}
return ref SpanIOHelper.GetFromMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length);
}
/// <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>
/// <param name="id">The index to use to get the <see cref="SplitterState"/>.</param>
/// <param name="destinationId">The index of the <see cref="SplitterDestination"/>.</param>
/// <returns>A <see cref="SplitterDestination"/>.</returns>
public SplitterDestination GetDestination(int id, int destinationId)
/// <returns>A <see cref="Span{SplitterDestination}"/>.</returns>
public Span<SplitterDestination> GetDestination(int id, int destinationId)
{
ref SplitterState splitter = ref GetState(id);
return splitter.GetData(destinationId);
}
/// <summary>
/// Gets the biquad filter state for a given splitter destination.
/// </summary>
/// <param name="destination">The splitter destination.</param>
/// <returns>Biquad filter state for the specified destination.</returns>
public Memory<BiquadFilterState> GetBiquadFilterState(SplitterDestination destination)
{
return _splitterBqfStates.Slice(destination.Id * BqfStatesPerDestination, BqfStatesPerDestination);
}
/// <summary>
/// Return true if the audio renderer has any splitters.
/// </summary>
/// <returns>True if the audio renderer has any splitters.</returns>
public bool UsingSplitter()
{
return !_splitters.IsEmpty && (!_splitterDestinationsV1.IsEmpty || !_splitterDestinationsV2.IsEmpty);
return !_splitters.IsEmpty && !_splitterDestinations.IsEmpty;
}
/// <summary>

View file

@ -1,199 +1,115 @@
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.
/// </summary>
public ref struct SplitterDestination
[StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)]
public struct SplitterDestination
{
private ref SplitterDestinationVersion1 _v1;
private ref SplitterDestinationVersion2 _v2;
public const int Alignment = 0x10;
/// <summary>
/// Checks if the splitter destination data reference is null.
/// The unique id of this <see cref="SplitterDestination"/>.
/// </summary>
public bool IsNull => Unsafe.IsNullRef(ref _v1) && Unsafe.IsNullRef(ref _v2);
/// <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;
}
}
}
public int Id;
/// <summary>
/// The mix to output the result of the splitter.
/// </summary>
public int DestinationId
{
get
{
if (Unsafe.IsNullRef(ref _v2))
{
if (Unsafe.IsNullRef(ref _v1))
{
return 0;
}
else
{
return _v1.DestinationId;
}
}
else
{
return _v2.DestinationId;
}
}
}
public int DestinationId;
/// <summary>
/// Mix buffer volumes storage.
/// </summary>
private MixArray _mix;
private MixArray _previousMix;
/// <summary>
/// Pointer to the next linked element.
/// </summary>
private unsafe SplitterDestination* _next;
/// <summary>
/// Set to true if in use.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool IsUsed;
/// <summary>
/// Set to true if the internal state need to be updated.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool NeedToUpdateInternalState;
[StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)]
private struct MixArray { }
/// <summary>
/// Mix buffer volumes.
/// </summary>
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
public Span<float> MixBufferVolume
{
get
{
if (Unsafe.IsNullRef(ref _v2))
{
if (Unsafe.IsNullRef(ref _v1))
{
return Span<float>.Empty;
}
else
{
return _v1.MixBufferVolume;
}
}
else
{
return _v2.MixBufferVolume;
}
}
}
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
{
get
{
if (Unsafe.IsNullRef(ref _v2))
{
if (Unsafe.IsNullRef(ref _v1))
{
return Span<float>.Empty;
}
else
{
return _v1.PreviousMixBufferVolume;
}
}
else
{
return _v2.PreviousMixBufferVolume;
}
}
}
public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
/// <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>
public readonly SplitterDestination Next
public readonly Span<SplitterDestination> Next
{
get
{
unsafe
{
if (Unsafe.IsNullRef(ref _v2))
{
if (Unsafe.IsNullRef(ref _v1))
{
return new SplitterDestination();
}
else
{
return new SplitterDestination(ref _v1.Next);
}
}
else
{
return new SplitterDestination(ref _v2.Next);
}
return _next != null ? new Span<SplitterDestination>(_next, 1) : Span<SplitterDestination>.Empty;
}
}
}
/// <summary>
/// Creates a new splitter destination wrapper for the version 1 splitter destination data.
/// Create a new <see cref="SplitterDestination"/>.
/// </summary>
/// <param name="v1">Version 1 splitter destination data</param>
public SplitterDestination(ref SplitterDestinationVersion1 v1)
/// <param name="id">The unique id of this <see cref="SplitterDestination"/>.</param>
public SplitterDestination(int id) : this()
{
_v1 = ref v1;
_v2 = ref Unsafe.NullRef<SplitterDestinationVersion2>();
Id = id;
DestinationId = Constants.UnusedMixId;
ClearVolumes();
}
/// <summary>
/// Creates a new splitter destination wrapper for the version 2 splitter destination data.
/// </summary>
/// <param name="v2">Version 2 splitter destination data</param>
public SplitterDestination(ref SplitterDestinationVersion2 v2)
{
_v1 = ref Unsafe.NullRef<SplitterDestinationVersion1>();
_v2 = ref v2;
}
/// <summary>
/// Creates a new splitter destination wrapper for the splitter destination data.
/// </summary>
/// <param name="v1">Version 1 splitter destination data</param>
/// <param name="v2">Version 2 splitter destination data</param>
public unsafe SplitterDestination(SplitterDestinationVersion1* v1, SplitterDestinationVersion2* v2)
{
_v1 = ref Unsafe.AsRef<SplitterDestinationVersion1>(v1);
_v2 = ref Unsafe.AsRef<SplitterDestinationVersion2>(v2);
}
/// <summary>
/// Update the splitter destination data from user parameter.
/// Update the <see cref="SplitterDestination"/> from user parameter.
/// </summary>
/// <param name="parameter">The user parameter.</param>
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) 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, isPrevVolumeResetSupported);
}
else
{
_v2.Update(parameter, isPrevVolumeResetSupported);
DestinationId = parameter.DestinationId;
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
if (!IsUsed && parameter.IsUsed)
{
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
NeedToUpdateInternalState = false;
}
IsUsed = parameter.IsUsed;
}
}
@ -202,14 +118,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void UpdateInternalState()
{
if (Unsafe.IsNullRef(ref _v2))
if (IsUsed && NeedToUpdateInternalState)
{
_v1.UpdateInternalState();
}
else
{
_v2.UpdateInternalState();
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
}
NeedToUpdateInternalState = false;
}
/// <summary>
@ -217,23 +131,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void MarkAsNeedToUpdateInternalState()
{
if (Unsafe.IsNullRef(ref _v2))
{
_v1.MarkAsNeedToUpdateInternalState();
}
else
{
_v2.MarkAsNeedToUpdateInternalState();
}
NeedToUpdateInternalState = true;
}
/// <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>
/// <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()
{
return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured();
return IsUsed && DestinationId != Constants.UnusedMixId;
}
/// <summary>
@ -243,17 +150,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>The volume for the given destination.</returns>
public float GetMixVolume(int destinationIndex)
{
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex);
}
Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
/// <summary>
/// Get the previous volume for a given destination.
/// </summary>
/// <param name="destinationIndex">The destination index to use.</param>
/// <returns>The volume for the given destination.</returns>
public float GetMixVolumePrev(int destinationIndex)
{
return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex);
return MixBufferVolume[destinationIndex];
}
/// <summary>
@ -261,33 +160,22 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void ClearVolumes()
{
if (Unsafe.IsNullRef(ref _v2))
{
_v1.ClearVolumes();
}
else
{
_v2.ClearVolumes();
}
MixBufferVolume.Clear();
PreviousMixBufferVolume.Clear();
}
/// <summary>
/// Link the next element to the given splitter destination.
/// Link the next element to the given <see cref="SplitterDestination"/>.
/// </summary>
/// <param name="next">The given splitter destination to link.</param>
public void Link(SplitterDestination next)
/// <param name="next">The given <see cref="SplitterDestination"/> to link.</param>
public void Link(ref SplitterDestination next)
{
if (Unsafe.IsNullRef(ref _v2))
unsafe
{
Debug.Assert(!Unsafe.IsNullRef(ref next._v1));
_v1.Link(ref next._v1);
}
else
{
Debug.Assert(!Unsafe.IsNullRef(ref next._v2));
_v2.Link(ref next._v2);
fixed (SplitterDestination* nextPtr = &next)
{
_next = nextPtr;
}
}
}
@ -296,74 +184,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
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;
}
}
}

View file

@ -1,208 +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>
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
{
Debug.Assert(Id == parameter.Id);
if (parameter.IsMagicValid() && Id == parameter.Id)
{
DestinationId = parameter.DestinationId;
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
if (resetPrevVolume)
{
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;
}
}
}
}

View file

@ -1,252 +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>
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
{
Debug.Assert(Id == parameter.Id);
if (parameter.IsMagicValid() && Id == parameter.Id)
{
DestinationId = parameter.DestinationId;
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
_biquadFilters = parameter.BiquadFilters;
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
if (resetPrevVolume)
{
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;
}
}
}

View file

@ -15,8 +15,6 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
public const int Alignment = 0x10;
private delegate void SplitterDestinationAction(SplitterDestination destination, int index);
/// <summary>
/// The unique id of this <see cref="SplitterState"/>.
/// </summary>
@ -28,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
public uint SampleRate;
/// <summary>
/// Count of splitter destinations.
/// Count of splitter destinations (<see cref="SplitterDestination"/>).
/// </summary>
public int DestinationCount;
@ -39,25 +37,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
public bool HasNewConnection;
/// <summary>
/// Linked list of <see cref="SplitterDestinationVersion1"/>.
/// Linked list of <see cref="SplitterDestination"/>.
/// </summary>
private unsafe SplitterDestinationVersion1* _destinationDataV1;
private unsafe SplitterDestination* _destinationsData;
/// <summary>
/// Linked list of <see cref="SplitterDestinationVersion2"/>.
/// Span to the first element of the linked list of <see cref="SplitterDestination"/>.
/// </summary>
private unsafe SplitterDestinationVersion2* _destinationDataV2;
/// <summary>
/// First element of the linked list of splitter destinations data.
/// </summary>
public readonly SplitterDestination Destination
public readonly Span<SplitterDestination> Destinations
{
get
{
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;
}
public readonly SplitterDestination GetData(int index)
public readonly Span<SplitterDestination> GetData(int index)
{
int i = 0;
SplitterDestination result = Destination;
Span<SplitterDestination> result = Destinations;
while (i < index)
{
if (result.IsNull)
if (result.IsEmpty)
{
break;
}
result = result.Next;
result = result[0].Next;
i++;
}
@ -100,25 +93,25 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
}
/// <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>
/// <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;
while (true)
{
if (temp.IsNull)
if (temp.IsEmpty)
{
break;
}
SplitterDestination next = temp.Next;
Span<SplitterDestination> next = temp[0].Next;
action(temp, i++);
action.Invoke(temp, i++);
temp = next;
}
@ -149,9 +142,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
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;
@ -159,9 +152,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
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;
}
}
@ -181,21 +174,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
}
/// <summary>
/// Set the head of the linked list of <see cref="Destination"/>.
/// Set the head of the linked list of <see cref="Destinations"/>.
/// </summary>
/// <param name="newValue">New destination value.</param>
public void SetDestination(SplitterDestination newValue)
/// <param name="newValue">A reference to a <see cref="SplitterDestination"/>.</param>
public void SetDestination(ref SplitterDestination newValue)
{
unsafe
{
fixed (SplitterDestinationVersion1* newValuePtr = &newValue.GetV1RefOrNull())
fixed (SplitterDestination* newValuePtr = &newValue)
{
_destinationDataV1 = newValuePtr;
}
fixed (SplitterDestinationVersion2* newValuePtr = &newValue.GetV2RefOrNull())
{
_destinationDataV2 = newValuePtr;
_destinationsData = newValuePtr;
}
}
}
@ -205,20 +193,19 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public readonly void UpdateInternalState()
{
ForEachDestination((destination, _) => destination.UpdateInternalState());
ForEachDestination((destination, _) => destination[0].UpdateInternalState());
}
/// <summary>
/// Clear all links from the <see cref="Destination"/>.
/// Clear all links from the <see cref="Destinations"/>.
/// </summary>
public void ClearLinks()
{
ForEachDestination((destination, _) => destination.Unlink());
ForEachDestination((destination, _) => destination[0].Unlink());
unsafe
{
_destinationDataV1 = null;
_destinationDataV2 = null;
_destinationsData = (SplitterDestination*)IntPtr.Zero;
}
}
@ -232,8 +219,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
unsafe
{
splitter._destinationDataV1 = null;
splitter._destinationDataV2 = null;
splitter._destinationsData = (SplitterDestination*)IntPtr.Zero;
}
splitter.DestinationCount = 0;

View file

@ -1,33 +1,13 @@
using Ryujinx.Common.Utilities;
using System;
namespace Ryujinx.Common.GraphicsDriver
{
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)
{
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
try
{

View file

@ -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);
}
}
}
}
}

View 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);
}
}
}

View file

@ -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.");
}
}
}

View file

@ -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>());
}
}
}

View file

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using System.Reflection;
@ -42,14 +42,14 @@ namespace Ryujinx.Common
return StreamUtils.StreamToBytes(stream);
}
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
{
var (assembly, path) = ResolveManifestPath(filename);
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);

View file

@ -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);
}
}
}
}

View file

@ -1,5 +1,6 @@
using Microsoft.IO;
using Ryujinx.Common.Memory;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -15,7 +16,7 @@ namespace Ryujinx.Common.Utilities
return output.ToArray();
}
public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
{
if (input is MemoryStream inputMemoryStream)
{
@ -25,9 +26,9 @@ namespace Ryujinx.Common.Utilities
{
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;
@ -65,14 +66,14 @@ namespace Ryujinx.Common.Utilities
return stream.ToArray();
}
private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
{
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.
_ = input.Read(ownedMemory.Span);
_ = input.Read(ownedMemory.Memory.Span);
return ownedMemory;
}

View file

@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
}
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);
}

View file

@ -1,5 +1,6 @@
using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{

View file

@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (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) =>
{
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);
}
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);
}
EmitSaturateUq(context, d, 16, e != 0);
EmitSaturateUnsignedRange(context, d, 16);
});
}
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);
EmitSaturateUqsub(context, d, 16);
EmitSaturateUnsignedRange(context, d, 16);
});
}
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);
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)
{
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)
private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo)
{
Debug.Assert(saturateTo <= 32);
@ -389,7 +379,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
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;
@ -397,7 +387,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
// 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;
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -144,9 +145,9 @@ namespace Ryujinx.Graphics.Device
}
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);
}

View file

@ -39,10 +39,7 @@ namespace Ryujinx.Graphics.Device
{
var field = fields[fieldIndex];
var currentFieldOffset = (int)Marshal.OffsetOf<TState>(field.Name);
var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf<TState>() : (int)Marshal.OffsetOf<TState>(fields[fieldIndex + 1].Name);
int sizeOfField = nextFieldOffset - currentFieldOffset;
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
{

View 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."),
};
}
}
}

View file

@ -6,13 +6,8 @@ namespace Ryujinx.Graphics.GAL
public enum BufferAccess
{
Default = 0,
HostMemory = 1,
DeviceMemory = 2,
DeviceMemoryMapped = 3,
MemoryTypeMask = 0xf,
Stream = 1 << 4,
SparseCompatible = 1 << 5,
FlushPersistent = 1 << 0,
Stream = 1 << 1,
SparseCompatible = 1 << 2,
}
}

View file

@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.GAL
{
public readonly TargetApi Api;
public readonly string VendorName;
public readonly SystemMemoryType MemoryType;
public readonly bool HasFrontFacingBug;
public readonly bool HasVectorIndexingBug;
@ -37,7 +36,6 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsMismatchingViewFormat;
public readonly bool SupportsCubemapView;
public readonly bool SupportsNonConstantTextureOffset;
public readonly bool SupportsQuads;
public readonly bool SupportsSeparateSampler;
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
@ -51,13 +49,6 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsIndirectParameters;
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 MaximumStorageBuffersPerStage;
public readonly uint MaximumTexturesPerStage;
@ -71,12 +62,9 @@ namespace Ryujinx.Graphics.GAL
public readonly int GatherBiasPrecision;
public readonly ulong MaximumGpuMemory;
public Capabilities(
TargetApi api,
string vendorName,
SystemMemoryType memoryType,
bool hasFrontFacingBug,
bool hasVectorIndexingBug,
bool needsFragmentOutputSpecialization,
@ -105,7 +93,6 @@ namespace Ryujinx.Graphics.GAL
bool supportsMismatchingViewFormat,
bool supportsCubemapView,
bool supportsNonConstantTextureOffset,
bool supportsQuads,
bool supportsSeparateSampler,
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
@ -118,12 +105,6 @@ namespace Ryujinx.Graphics.GAL
bool supportsViewportSwizzle,
bool supportsIndirectParameters,
bool supportsDepthClipControl,
int uniformBufferSetIndex,
int storageBufferSetIndex,
int textureSetIndex,
int imageSetIndex,
int extraSetBaseIndex,
int maximumExtraSets,
uint maximumUniformBuffersPerStage,
uint maximumStorageBuffersPerStage,
uint maximumTexturesPerStage,
@ -133,12 +114,10 @@ namespace Ryujinx.Graphics.GAL
int shaderSubgroupSize,
int storageBufferOffsetAlignment,
int textureBufferOffsetAlignment,
int gatherBiasPrecision,
ulong maximumGpuMemory)
int gatherBiasPrecision)
{
Api = api;
VendorName = vendorName;
MemoryType = memoryType;
HasFrontFacingBug = hasFrontFacingBug;
HasVectorIndexingBug = hasVectorIndexingBug;
NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
@ -167,7 +146,6 @@ namespace Ryujinx.Graphics.GAL
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsCubemapView = supportsCubemapView;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
SupportsQuads = supportsQuads;
SupportsSeparateSampler = supportsSeparateSampler;
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
@ -180,12 +158,6 @@ namespace Ryujinx.Graphics.GAL
SupportsViewportSwizzle = supportsViewportSwizzle;
SupportsIndirectParameters = supportsIndirectParameters;
SupportsDepthClipControl = supportsDepthClipControl;
UniformBufferSetIndex = uniformBufferSetIndex;
StorageBufferSetIndex = storageBufferSetIndex;
TextureSetIndex = textureSetIndex;
ImageSetIndex = imageSetIndex;
ExtraSetBaseIndex = extraSetBaseIndex;
MaximumExtraSets = maximumExtraSets;
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
MaximumTexturesPerStage = maximumTexturesPerStage;
@ -196,7 +168,6 @@ namespace Ryujinx.Graphics.GAL
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
GatherBiasPrecision = gatherBiasPrecision;
MaximumGpuMemory = maximumGpuMemory;
}
}
}

View file

@ -711,36 +711,5 @@ namespace Ryujinx.Graphics.GAL
{
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;
}
}
}

View file

@ -1,9 +1,8 @@
using System;
namespace Ryujinx.Graphics.GAL
{
public interface IImageArray : IDisposable
public interface IImageArray
{
void SetFormats(int index, Format[] imageFormats);
void SetImages(int index, ITexture[] images);
}
}

View file

@ -58,9 +58,8 @@ namespace Ryujinx.Graphics.GAL
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 SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
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 SetTextureArray(ShaderStage stage, int binding, ITextureArray array);
void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array);
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers);

View file

@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.GAL
void BackgroundContextAction(Action action, bool alwaysBackground = false);
BufferHandle CreateBuffer(int size, BufferAccess access = BufferAccess.Default);
BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint);
BufferHandle CreateBuffer(nint pointer, int size);
BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers);

View file

@ -1,4 +1,4 @@
using Ryujinx.Common.Memory;
using System.Buffers;
namespace Ryujinx.Graphics.GAL
{
@ -18,30 +18,30 @@ namespace Ryujinx.Graphics.GAL
PinnedSpan<byte> GetData(int layer, int level);
/// <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.
/// </summary>
/// <param name="data">Texture data bytes</param>
void SetData(MemoryOwner<byte> data);
void SetData(IMemoryOwner<byte> data);
/// <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.
/// </summary>
/// <param name="data">Texture data bytes</param>
/// <param name="layer">Target layer</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>
/// 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.
/// </summary>
/// <param name="data">Texture data bytes</param>
/// <param name="layer">Target layer</param>
/// <param name="level">Target level</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);

View file

@ -1,8 +1,6 @@
using System;
namespace Ryujinx.Graphics.GAL
{
public interface ITextureArray : IDisposable
public interface ITextureArray
{
void SetSamplers(int index, ISampler[] samplers);
void SetTextures(int index, ITexture[] textures);

View file

@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
}
Register<ActionCommand>(CommandType.Action);
Register<CreateBufferCommand>(CommandType.CreateBuffer);
Register<CreateBufferAccessCommand>(CommandType.CreateBufferAccess);
Register<CreateBufferSparseCommand>(CommandType.CreateBufferSparse);
Register<CreateHostBufferCommand>(CommandType.CreateHostBuffer);
@ -66,7 +67,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose);
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
Register<ImageArraySetFormatsCommand>(CommandType.ImageArraySetFormats);
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
@ -88,7 +89,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<TextureSetDataSliceRegionCommand>(CommandType.TextureSetDataSliceRegion);
Register<TextureSetStorageCommand>(CommandType.TextureSetStorage);
Register<TextureArrayDisposeCommand>(CommandType.TextureArrayDispose);
Register<TextureArraySetSamplersCommand>(CommandType.TextureArraySetSamplers);
Register<TextureArraySetTexturesCommand>(CommandType.TextureArraySetTextures);
@ -125,7 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<SetUniformBuffersCommand>(CommandType.SetUniformBuffers);
Register<SetImageCommand>(CommandType.SetImage);
Register<SetImageArrayCommand>(CommandType.SetImageArray);
Register<SetImageArraySeparateCommand>(CommandType.SetImageArraySeparate);
Register<SetIndexBufferCommand>(CommandType.SetIndexBuffer);
Register<SetLineParametersCommand>(CommandType.SetLineParameters);
Register<SetLogicOpStateCommand>(CommandType.SetLogicOpState);
@ -143,7 +142,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<SetStencilTestCommand>(CommandType.SetStencilTest);
Register<SetTextureAndSamplerCommand>(CommandType.SetTextureAndSampler);
Register<SetTextureArrayCommand>(CommandType.SetTextureArray);
Register<SetTextureArraySeparateCommand>(CommandType.SetTextureArraySeparate);
Register<SetUserClipDistanceCommand>(CommandType.SetUserClipDistance);
Register<SetVertexAttribsCommand>(CommandType.SetVertexAttribs);
Register<SetVertexBuffersCommand>(CommandType.SetVertexBuffers);

View file

@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
enum CommandType : byte
{
Action,
CreateBuffer,
CreateBufferAccess,
CreateBufferSparse,
CreateHostBuffer,
@ -26,7 +27,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
CounterEventDispose,
CounterEventFlush,
ImageArrayDispose,
ImageArraySetFormats,
ImageArraySetImages,
ProgramDispose,
@ -48,7 +49,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
TextureSetDataSliceRegion,
TextureSetStorage,
TextureArrayDispose,
TextureArraySetSamplers,
TextureArraySetTextures,
@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetUniformBuffers,
SetImage,
SetImageArray,
SetImageArraySeparate,
SetIndexBuffer,
SetLineParameters,
SetLogicOpState,
@ -103,7 +102,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetStencilTest,
SetTextureAndSampler,
SetTextureArray,
SetTextureArraySeparate,
SetUserClipDistance,
SetVertexAttribs,
SetVertexBuffers,

View file

@ -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();
}
}
}

View file

@ -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));
}
}
}

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -10,17 +10,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
private ShaderStage _stage;
private int _binding;
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;
_binding = binding;
_texture = texture;
_imageFormat = imageFormat;
}
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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using System.Buffers;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
public readonly CommandType CommandType => CommandType.TextureSetData;
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;
_data = data;

View file

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using System.Buffers;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
private TableRef<ThreadedTexture> _texture;
private TableRef<MemoryOwner<byte>> _data;
private TableRef<IMemoryOwner<byte>> _data;
private int _layer;
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;
_data = data;

View file

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using System.Buffers;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
private TableRef<ThreadedTexture> _texture;
private TableRef<MemoryOwner<byte>> _data;
private TableRef<IMemoryOwner<byte>> _data;
private int _layer;
private int _level;
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;
_data = data;

View file

@ -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();
}
}
}

View file

@ -21,9 +21,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
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();
}

View file

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
using Ryujinx.Graphics.GAL.Multithreading.Model;
using System.Buffers;
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
{
@ -111,21 +111,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
}
/// <inheritdoc/>
public void SetData(MemoryOwner<byte> data)
public void SetData(IMemoryOwner<byte> data)
{
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
_renderer.QueueCommand();
}
/// <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.QueueCommand();
}
/// <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.QueueCommand();

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