forked from Mirror/Ryujinx
Compare commits
22 commits
dependabot
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
a2c0035013 | ||
|
7d158acc3b | ||
|
5dbba07e33 | ||
|
d86249cb0a | ||
|
04d68ca616 | ||
|
050f22977f | ||
|
319507f2a1 | ||
|
d717aef2be | ||
|
24ee8c39f1 | ||
|
73f985d27c | ||
|
ef81658fbd | ||
|
062ef43eb4 | ||
|
eb8132b627 | ||
|
ccf96bf5e6 | ||
|
f39e89ece7 | ||
|
cf77c011e4 | ||
|
cd74ae1bbd | ||
|
62216782ca | ||
|
2f36a6665c | ||
|
ca59c3f499 | ||
|
fdd7ee791c | ||
|
398fa1c238 |
140 changed files with 2667 additions and 835 deletions
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -23,7 +23,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Log file
|
label: Log file
|
||||||
description: A log file will help our developers to better diagnose and fix the issue.
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
|
|
|
@ -39,9 +39,9 @@
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
|
|
|
@ -746,6 +746,7 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
||||||
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||||
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||||
|
SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
|
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
||||||
|
@ -1034,6 +1035,7 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
||||||
|
SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 encoding.
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
|
|
|
@ -292,6 +292,16 @@ namespace ARMeilleure.Instructions
|
||||||
EmitAluStore(context, res);
|
EmitAluStore(context, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Qadd16(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||||
|
|
||||||
|
SetIntA32(context, op.Rd, EmitSigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||||
|
{
|
||||||
|
EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Rbit(ArmEmitterContext context)
|
public static void Rbit(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
Operand m = GetAluM(context);
|
Operand m = GetAluM(context);
|
||||||
|
@ -976,6 +986,94 @@ 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)
|
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||||
{
|
{
|
||||||
Debug.Assert(saturateTo <= 32);
|
Debug.Assert(saturateTo <= 32);
|
||||||
|
@ -1053,6 +1151,21 @@ namespace ARMeilleure.Instructions
|
||||||
context.MarkLabel(lblExit);
|
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)
|
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||||
{
|
{
|
||||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||||
|
|
|
@ -106,6 +106,38 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Vshll2(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
|
||||||
|
Operand res = context.VectorZero();
|
||||||
|
|
||||||
|
int elems = op.GetBytesCount() >> op.Size;
|
||||||
|
|
||||||
|
for (int index = 0; index < elems; index++)
|
||||||
|
{
|
||||||
|
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
|
||||||
|
|
||||||
|
if (op.Size == 2)
|
||||||
|
{
|
||||||
|
if (op.U)
|
||||||
|
{
|
||||||
|
me = context.ZeroExtend32(OperandType.I64, me);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
me = context.SignExtend32(OperandType.I64, me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
me = context.ShiftLeft(me, Const(8 << op.Size));
|
||||||
|
|
||||||
|
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Vshr(ArmEmitterContext context)
|
public static void Vshr(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
|
|
@ -527,6 +527,7 @@ namespace ARMeilleure.Instructions
|
||||||
Pld,
|
Pld,
|
||||||
Pop,
|
Pop,
|
||||||
Push,
|
Push,
|
||||||
|
Qadd16,
|
||||||
Rev,
|
Rev,
|
||||||
Revsh,
|
Revsh,
|
||||||
Rsb,
|
Rsb,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
|
@ -11,11 +10,10 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
public IntPtr FuncPtr { get; }
|
public IntPtr FuncPtr { get; }
|
||||||
|
|
||||||
public DelegateInfo(Delegate dlg)
|
public DelegateInfo(Delegate dlg, IntPtr funcPtr)
|
||||||
{
|
{
|
||||||
_dlg = dlg;
|
_dlg = dlg;
|
||||||
|
FuncPtr = funcPtr;
|
||||||
FuncPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(dlg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using ARMeilleure.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
|
@ -64,11 +65,11 @@ namespace ARMeilleure.Translation
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetDelegateInfo(Delegate dlg)
|
private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr)
|
||||||
{
|
{
|
||||||
string key = GetKey(dlg.Method);
|
string key = GetKey(dlg.Method);
|
||||||
|
|
||||||
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
_delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetKey(MethodInfo info)
|
private static string GetKey(MethodInfo info)
|
||||||
|
@ -82,179 +83,353 @@ namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
_delegates = new SortedList<string, DelegateInfo>();
|
_delegates = new SortedList<string, DelegateInfo>();
|
||||||
|
|
||||||
SetDelegateInfo(new MathAbs(Math.Abs));
|
var dlgMathAbs = new MathAbs(Math.Abs);
|
||||||
SetDelegateInfo(new MathCeiling(Math.Ceiling));
|
var dlgMathCeiling = new MathCeiling(Math.Ceiling);
|
||||||
SetDelegateInfo(new MathFloor(Math.Floor));
|
var dlgMathFloor = new MathFloor(Math.Floor);
|
||||||
SetDelegateInfo(new MathRound(Math.Round));
|
var dlgMathRound = new MathRound(Math.Round);
|
||||||
SetDelegateInfo(new MathTruncate(Math.Truncate));
|
var dlgMathTruncate = new MathTruncate(Math.Truncate);
|
||||||
|
|
||||||
SetDelegateInfo(new MathFAbs(MathF.Abs));
|
var dlgMathFAbs = new MathFAbs(MathF.Abs);
|
||||||
SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
|
var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling);
|
||||||
SetDelegateInfo(new MathFFloor(MathF.Floor));
|
var dlgMathFFloor = new MathFFloor(MathF.Floor);
|
||||||
SetDelegateInfo(new MathFRound(MathF.Round));
|
var dlgMathFRound = new MathFRound(MathF.Round);
|
||||||
SetDelegateInfo(new MathFTruncate(MathF.Truncate));
|
var dlgMathFTruncate = new MathFTruncate(MathF.Truncate);
|
||||||
|
|
||||||
SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
|
var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break);
|
||||||
SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
|
var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization);
|
||||||
SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
|
var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit);
|
||||||
SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
|
var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0);
|
||||||
SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
|
var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0);
|
||||||
SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
|
var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0);
|
||||||
SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
|
var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0);
|
||||||
SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
|
var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0);
|
||||||
SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
|
var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress);
|
||||||
SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
|
var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine);
|
||||||
SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
|
var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte);
|
||||||
SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
|
var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16);
|
||||||
SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
|
var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32);
|
||||||
SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
|
var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64);
|
||||||
SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
|
var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128);
|
||||||
SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
|
var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking);
|
||||||
SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
|
var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall);
|
||||||
SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
|
var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess);
|
||||||
SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
|
var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined);
|
||||||
SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
|
var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte);
|
||||||
SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
|
var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16);
|
||||||
SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
|
var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32);
|
||||||
SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
|
var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64);
|
||||||
SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
|
var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128);
|
||||||
|
|
||||||
SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
|
var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns);
|
||||||
SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
|
var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
|
var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
|
var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
|
var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
|
var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
|
var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
|
var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
|
var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w);
|
||||||
SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
|
var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x);
|
||||||
SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
|
var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt);
|
||||||
SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
|
var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt);
|
||||||
SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
|
var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate);
|
||||||
SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
|
var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose);
|
||||||
SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
|
var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower);
|
||||||
SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
|
var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority);
|
||||||
SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
|
var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity);
|
||||||
SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
|
var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper);
|
||||||
SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
|
var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns);
|
||||||
SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
|
var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns);
|
||||||
SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
|
var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128);
|
||||||
SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
|
var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32);
|
||||||
SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
|
var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64);
|
||||||
SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
|
var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32);
|
||||||
SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
|
var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64);
|
||||||
SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
|
var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32);
|
||||||
SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
|
var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64);
|
||||||
SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
|
var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32);
|
||||||
SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
|
var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64);
|
||||||
SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
|
var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1);
|
||||||
SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
|
var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2);
|
||||||
SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
|
var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1);
|
||||||
SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
|
var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2);
|
||||||
SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
|
var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64);
|
||||||
SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
|
var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1);
|
||||||
SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
|
var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2);
|
||||||
SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
|
var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3);
|
||||||
SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
|
var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4);
|
||||||
SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
|
var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1);
|
||||||
SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
|
var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2);
|
||||||
SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
|
var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3);
|
||||||
SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
|
var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4);
|
||||||
SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
|
var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64);
|
||||||
|
|
||||||
SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
|
var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert);
|
||||||
SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
|
var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert);
|
||||||
|
|
||||||
SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
|
var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd);
|
||||||
SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
|
var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
|
var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare);
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
|
var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ);
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
|
var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
|
var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE);
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
|
var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
|
var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT);
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
|
var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
|
var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE);
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
|
var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
|
var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT);
|
||||||
SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
|
var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
|
var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv);
|
||||||
SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
|
var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax);
|
||||||
SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
|
var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
|
var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum);
|
||||||
SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
|
var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
|
var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin);
|
||||||
SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
|
var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
|
var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum);
|
||||||
SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
|
var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
|
var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul);
|
||||||
SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
|
var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
|
var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd);
|
||||||
SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
|
var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
|
var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub);
|
||||||
SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
|
var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
|
var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX);
|
||||||
SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
|
var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd);
|
||||||
SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
|
var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub);
|
||||||
SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
|
var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate);
|
||||||
SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
|
var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
|
var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
|
var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused);
|
||||||
SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
|
var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX);
|
||||||
SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
|
var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate);
|
||||||
SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
|
var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
|
var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
|
var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused);
|
||||||
SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
|
var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt);
|
||||||
SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
|
var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub);
|
||||||
|
|
||||||
SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
|
var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert);
|
||||||
|
|
||||||
SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
|
var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd);
|
||||||
SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
|
var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
|
var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare);
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
|
var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ);
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
|
var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
|
var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE);
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
|
var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
|
var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT);
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
|
var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
|
var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE);
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
|
var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
|
var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT);
|
||||||
SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
|
var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
|
var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv);
|
||||||
SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
|
var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax);
|
||||||
SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
|
var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
|
var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum);
|
||||||
SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
|
var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
|
var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin);
|
||||||
SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
|
var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
|
var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum);
|
||||||
SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
|
var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
|
var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul);
|
||||||
SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
|
var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
|
var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd);
|
||||||
SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
|
var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
|
var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub);
|
||||||
SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
|
var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
|
var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX);
|
||||||
SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
|
var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd);
|
||||||
SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
|
var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub);
|
||||||
SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
|
var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate);
|
||||||
SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
|
var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
|
var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
|
var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused);
|
||||||
SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
|
var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX);
|
||||||
SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
|
var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate);
|
||||||
SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
|
var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
|
var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only.
|
||||||
SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
|
var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused);
|
||||||
SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
|
var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt);
|
||||||
SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
|
var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub);
|
||||||
|
|
||||||
SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert));
|
var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert);
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate<MathAbs>(dlgMathAbs));
|
||||||
|
SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate<MathCeiling>(dlgMathCeiling));
|
||||||
|
SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate<MathFloor>(dlgMathFloor));
|
||||||
|
SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate<MathRound>(dlgMathRound));
|
||||||
|
SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate<MathTruncate>(dlgMathTruncate));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate<MathFAbs>(dlgMathFAbs));
|
||||||
|
SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate<MathFCeiling>(dlgMathFCeiling));
|
||||||
|
SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate<MathFFloor>(dlgMathFFloor));
|
||||||
|
SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate<MathFRound>(dlgMathFRound));
|
||||||
|
SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate<MathFTruncate>(dlgMathFTruncate));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate<NativeInterfaceBreak>(dlgNativeInterfaceBreak));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate<NativeInterfaceCheckSynchronization>(dlgNativeInterfaceCheckSynchronization));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate<NativeInterfaceEnqueueForRejit>(dlgNativeInterfaceEnqueueForRejit));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntfrqEl0>(dlgNativeInterfaceGetCntfrqEl0));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntpctEl0>(dlgNativeInterfaceGetCntpctEl0));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntvctEl0>(dlgNativeInterfaceGetCntvctEl0));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCtrEl0>(dlgNativeInterfaceGetCtrEl0));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetDczidEl0>(dlgNativeInterfaceGetDczidEl0));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetFunctionAddress>(dlgNativeInterfaceGetFunctionAddress));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate<NativeInterfaceInvalidateCacheLine>(dlgNativeInterfaceInvalidateCacheLine));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadByte>(dlgNativeInterfaceReadByte));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt16>(dlgNativeInterfaceReadUInt16));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt32>(dlgNativeInterfaceReadUInt32));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt64>(dlgNativeInterfaceReadUInt64));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadVector128>(dlgNativeInterfaceReadVector128));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSignalMemoryTracking>(dlgNativeInterfaceSignalMemoryTracking));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSupervisorCall>(dlgNativeInterfaceSupervisorCall));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate<NativeInterfaceThrowInvalidMemoryAccess>(dlgNativeInterfaceThrowInvalidMemoryAccess));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate<NativeInterfaceUndefined>(dlgNativeInterfaceUndefined));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteByte>(dlgNativeInterfaceWriteByte));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt16>(dlgNativeInterfaceWriteUInt16));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt32>(dlgNativeInterfaceWriteUInt32));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt64>(dlgNativeInterfaceWriteUInt64));
|
||||||
|
SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteVector128>(dlgNativeInterfaceWriteVector128));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingSigns>(dlgSoftFallbackCountLeadingSigns));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingZeros>(dlgSoftFallbackCountLeadingZeros));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32b>(dlgSoftFallbackCrc32b));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cb>(dlgSoftFallbackCrc32cb));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32ch>(dlgSoftFallbackCrc32ch));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cw>(dlgSoftFallbackCrc32cw));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cx>(dlgSoftFallbackCrc32cx));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32h>(dlgSoftFallbackCrc32h));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32w>(dlgSoftFallbackCrc32w));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32x>(dlgSoftFallbackCrc32x));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackDecrypt>(dlgSoftFallbackDecrypt));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackEncrypt>(dlgSoftFallbackEncrypt));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate<SoftFallbackFixedRotate>(dlgSoftFallbackFixedRotate));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashChoose>(dlgSoftFallbackHashChoose));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashLower>(dlgSoftFallbackHashLower));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashMajority>(dlgSoftFallbackHashMajority));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashParity>(dlgSoftFallbackHashParity));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashUpper>(dlgSoftFallbackHashUpper));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackInverseMixColumns>(dlgSoftFallbackInverseMixColumns));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackMixColumns>(dlgSoftFallbackMixColumns));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate<SoftFallbackPolynomialMult64_128>(dlgSoftFallbackPolynomialMult64_128));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS32>(dlgSoftFallbackSatF32ToS32));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS64>(dlgSoftFallbackSatF32ToS64));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU32>(dlgSoftFallbackSatF32ToU32));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU64>(dlgSoftFallbackSatF32ToU64));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS32>(dlgSoftFallbackSatF64ToS32));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS64>(dlgSoftFallbackSatF64ToS64));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU32>(dlgSoftFallbackSatF64ToU32));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU64>(dlgSoftFallbackSatF64ToU64));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart1>(dlgSoftFallbackSha1SchedulePart1));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart2>(dlgSoftFallbackSha1SchedulePart2));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart1>(dlgSoftFallbackSha256SchedulePart1));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart2>(dlgSoftFallbackSha256SchedulePart2));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSignedShrImm64>(dlgSoftFallbackSignedShrImm64));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl1>(dlgSoftFallbackTbl1));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl2>(dlgSoftFallbackTbl2));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl3>(dlgSoftFallbackTbl3));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl4>(dlgSoftFallbackTbl4));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx1>(dlgSoftFallbackTbx1));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx2>(dlgSoftFallbackTbx2));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx3>(dlgSoftFallbackTbx3));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx4>(dlgSoftFallbackTbx4));
|
||||||
|
SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackUnsignedShrImm64>(dlgSoftFallbackUnsignedShrImm64));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_32FPConvert>(dlgSoftFloat16_32FPConvert));
|
||||||
|
SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_64FPConvert>(dlgSoftFloat16_64FPConvert));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAdd>(dlgSoftFloat32FPAdd));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAddFpscr>(dlgSoftFloat32FPAddFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompare>(dlgSoftFloat32FPCompare));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQ>(dlgSoftFloat32FPCompareEQ));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQFpscr>(dlgSoftFloat32FPCompareEQFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGE>(dlgSoftFloat32FPCompareGE));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGEFpscr>(dlgSoftFloat32FPCompareGEFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGT>(dlgSoftFloat32FPCompareGT));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGTFpscr>(dlgSoftFloat32FPCompareGTFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLE>(dlgSoftFloat32FPCompareLE));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLEFpscr>(dlgSoftFloat32FPCompareLEFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLT>(dlgSoftFloat32FPCompareLT));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLTFpscr>(dlgSoftFloat32FPCompareLTFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPDiv>(dlgSoftFloat32FPDiv));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMax>(dlgSoftFloat32FPMax));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxFpscr>(dlgSoftFloat32FPMaxFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNum>(dlgSoftFloat32FPMaxNum));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNumFpscr>(dlgSoftFloat32FPMaxNumFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMin>(dlgSoftFloat32FPMin));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinFpscr>(dlgSoftFloat32FPMinFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNum>(dlgSoftFloat32FPMinNum));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNumFpscr>(dlgSoftFloat32FPMinNumFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMul>(dlgSoftFloat32FPMul));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulFpscr>(dlgSoftFloat32FPMulFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAdd>(dlgSoftFloat32FPMulAdd));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAddFpscr>(dlgSoftFloat32FPMulAddFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSub>(dlgSoftFloat32FPMulSub));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSubFpscr>(dlgSoftFloat32FPMulSubFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulX>(dlgSoftFloat32FPMulX));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulAdd>(dlgSoftFloat32FPNegMulAdd));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulSub>(dlgSoftFloat32FPNegMulSub));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimate>(dlgSoftFloat32FPRecipEstimate));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimateFpscr>(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStep>(dlgSoftFloat32FPRecipStep)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStepFused>(dlgSoftFloat32FPRecipStepFused));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecpX>(dlgSoftFloat32FPRecpX));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimate>(dlgSoftFloat32FPRSqrtEstimate));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimateFpscr>(dlgSoftFloat32FPRSqrtEstimateFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStep>(dlgSoftFloat32FPRSqrtStep)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStepFused>(dlgSoftFloat32FPRSqrtStepFused));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSqrt>(dlgSoftFloat32FPSqrt));
|
||||||
|
SetDelegateInfo(dlgSoftFloat32FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSub>(dlgSoftFloat32FPSub));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgSoftFloat32_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat32_16FPConvert>(dlgSoftFloat32_16FPConvert));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAdd>(dlgSoftFloat64FPAdd));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAddFpscr>(dlgSoftFloat64FPAddFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompare>(dlgSoftFloat64FPCompare));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQ>(dlgSoftFloat64FPCompareEQ));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQFpscr>(dlgSoftFloat64FPCompareEQFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGE>(dlgSoftFloat64FPCompareGE));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGEFpscr>(dlgSoftFloat64FPCompareGEFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGT>(dlgSoftFloat64FPCompareGT));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGTFpscr>(dlgSoftFloat64FPCompareGTFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLE>(dlgSoftFloat64FPCompareLE));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLEFpscr>(dlgSoftFloat64FPCompareLEFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLT>(dlgSoftFloat64FPCompareLT));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLTFpscr>(dlgSoftFloat64FPCompareLTFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPDiv>(dlgSoftFloat64FPDiv));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMax>(dlgSoftFloat64FPMax));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxFpscr>(dlgSoftFloat64FPMaxFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNum>(dlgSoftFloat64FPMaxNum));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNumFpscr>(dlgSoftFloat64FPMaxNumFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMin>(dlgSoftFloat64FPMin));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinFpscr>(dlgSoftFloat64FPMinFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNum>(dlgSoftFloat64FPMinNum));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNumFpscr>(dlgSoftFloat64FPMinNumFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMul>(dlgSoftFloat64FPMul));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulFpscr>(dlgSoftFloat64FPMulFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAdd>(dlgSoftFloat64FPMulAdd));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAddFpscr>(dlgSoftFloat64FPMulAddFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSub>(dlgSoftFloat64FPMulSub));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSubFpscr>(dlgSoftFloat64FPMulSubFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulX>(dlgSoftFloat64FPMulX));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulAdd>(dlgSoftFloat64FPNegMulAdd));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulSub>(dlgSoftFloat64FPNegMulSub));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimate>(dlgSoftFloat64FPRecipEstimate));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimateFpscr>(dlgSoftFloat64FPRecipEstimateFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStep>(dlgSoftFloat64FPRecipStep)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStepFused>(dlgSoftFloat64FPRecipStepFused));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecpX>(dlgSoftFloat64FPRecpX));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimate>(dlgSoftFloat64FPRSqrtEstimate));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimateFpscr>(dlgSoftFloat64FPRSqrtEstimateFpscr)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStep>(dlgSoftFloat64FPRSqrtStep)); // A32 only.
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStepFused>(dlgSoftFloat64FPRSqrtStepFused));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSqrt>(dlgSoftFloat64FPSqrt));
|
||||||
|
SetDelegateInfo(dlgSoftFloat64FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSub>(dlgSoftFloat64FPSub));
|
||||||
|
|
||||||
|
SetDelegateInfo(dlgSoftFloat64_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat64_16FPConvert>(dlgSoftFloat64_16FPConvert));
|
||||||
}
|
}
|
||||||
|
|
||||||
private delegate double MathAbs(double value);
|
private delegate double MathAbs(double value);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Server.Effect;
|
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
{
|
{
|
||||||
|
@ -21,18 +23,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
public CompressorParameter Parameter => _parameter;
|
public CompressorParameter Parameter => _parameter;
|
||||||
public Memory<CompressorState> State { get; }
|
public Memory<CompressorState> State { get; }
|
||||||
|
public Memory<EffectResultState> ResultState { get; }
|
||||||
public ushort[] OutputBufferIndices { get; }
|
public ushort[] OutputBufferIndices { get; }
|
||||||
public ushort[] InputBufferIndices { get; }
|
public ushort[] InputBufferIndices { get; }
|
||||||
public bool IsEffectEnabled { get; }
|
public bool IsEffectEnabled { get; }
|
||||||
|
|
||||||
private CompressorParameter _parameter;
|
private CompressorParameter _parameter;
|
||||||
|
|
||||||
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> resultState, bool isEnabled, int nodeId)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
_parameter = parameter;
|
_parameter = parameter;
|
||||||
State = state;
|
State = state;
|
||||||
|
ResultState = resultState;
|
||||||
|
|
||||||
IsEffectEnabled = isEnabled;
|
IsEffectEnabled = isEnabled;
|
||||||
|
|
||||||
|
@ -71,9 +75,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
|
||||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
{
|
||||||
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
|
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];
|
||||||
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
||||||
float unknown4 = state.Unknown4;
|
float unknown4 = state.Unknown4;
|
||||||
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
||||||
|
@ -92,7 +103,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
float mean = FloatingPointHelper.MeanSquare(channelInput);
|
||||||
|
float newMean = inputMovingAverage.Update(mean, _parameter.InputGain);
|
||||||
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
||||||
float z = 1.0f;
|
float z = 1.0f;
|
||||||
|
|
||||||
|
@ -111,7 +123,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
if (y >= state.Unknown14)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -126,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
if ((unknown4 - z) <= 0.08f)
|
if ((unknown4 - z) <= 0.08f)
|
||||||
{
|
{
|
||||||
compressionEmaAlpha = Parameter.ReleaseCoefficient;
|
compressionEmaAlpha = _parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if ((unknown4 - z) >= -0.08f)
|
if ((unknown4 - z) >= -0.08f)
|
||||||
{
|
{
|
||||||
|
@ -140,18 +152,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
compressionEmaAlpha = Parameter.AttackCoefficient;
|
compressionEmaAlpha = _parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
|
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;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
unknown4 = unknown4New;
|
unknown4 = unknown4New;
|
||||||
previousCompressionEmaAlpha = compressionEmaAlpha;
|
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;
|
state.InputMovingAverage = inputMovingAverage;
|
||||||
|
@ -161,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,10 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,11 +51,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
if (IsEffectEnabled)
|
if (IsEffectEnabled)
|
||||||
{
|
{
|
||||||
if (Parameter.Status == UsageState.Invalid)
|
if (_parameter.Status == UsageState.Invalid)
|
||||||
{
|
{
|
||||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||||
}
|
}
|
||||||
else if (Parameter.Status == UsageState.New)
|
else if (_parameter.Status == UsageState.New)
|
||||||
{
|
{
|
||||||
LimiterState.UpdateParameter(ref _parameter);
|
LimiterState.UpdateParameter(ref _parameter);
|
||||||
}
|
}
|
||||||
|
@ -66,56 +66,56 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
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> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||||
Span<IntPtr> outputBuffers = 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]);
|
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[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++)
|
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||||
{
|
{
|
||||||
float rawInputSample = *((float*)inputBuffers[channelIndex] + 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 sampleInputMax = Math.Abs(inputSample);
|
||||||
|
|
||||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
float inputCoefficient = _parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||||
{
|
{
|
||||||
inputCoefficient = Parameter.AttackCoefficient;
|
inputCoefficient = _parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||||
float attenuation = 1.0f;
|
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)
|
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||||
{
|
{
|
||||||
outputCoefficient = Parameter.AttackCoefficient;
|
outputCoefficient = _parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
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;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||||
|
|
||||||
|
@ -123,16 +123,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
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
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,11 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
if (IsEffectEnabled)
|
if (IsEffectEnabled)
|
||||||
{
|
{
|
||||||
if (Parameter.Status == UsageState.Invalid)
|
if (_parameter.Status == UsageState.Invalid)
|
||||||
{
|
{
|
||||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||||
}
|
}
|
||||||
else if (Parameter.Status == UsageState.New)
|
else if (_parameter.Status == UsageState.New)
|
||||||
{
|
{
|
||||||
LimiterState.UpdateParameter(ref _parameter);
|
LimiterState.UpdateParameter(ref _parameter);
|
||||||
}
|
}
|
||||||
|
@ -77,63 +77,63 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
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];
|
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||||
|
|
||||||
statistics.Reset();
|
statistics.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||||
Span<IntPtr> outputBuffers = 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]);
|
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[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++)
|
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||||
{
|
{
|
||||||
float rawInputSample = *((float*)inputBuffers[channelIndex] + 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 sampleInputMax = Math.Abs(inputSample);
|
||||||
|
|
||||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
float inputCoefficient = _parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||||
{
|
{
|
||||||
inputCoefficient = Parameter.AttackCoefficient;
|
inputCoefficient = _parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||||
float attenuation = 1.0f;
|
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)
|
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||||
{
|
{
|
||||||
outputCoefficient = Parameter.AttackCoefficient;
|
outputCoefficient = _parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
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;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||||
|
|
||||||
|
@ -141,9 +141,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
|
||||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
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)
|
if (!ResultState.IsEmpty)
|
||||||
|
@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
{
|
{
|
||||||
|
|
|
@ -90,9 +90,16 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||||
public bool MakeupGainEnabled;
|
public bool MakeupGainEnabled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reserved/padding.
|
/// Indicate if the compressor effect should output statistics.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Array2<byte> _reserved;
|
[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;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the <see cref="ChannelCount"/> is valid.
|
/// Check if the <see cref="ChannelCount"/> is valid.
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,11 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsUsed { get; }
|
bool IsUsed { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to true to force resetting the previous mix volumes.
|
||||||
|
/// </summary>
|
||||||
|
bool ResetPrevVolume { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mix buffer volumes.
|
/// Mix buffer volumes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -37,10 +37,16 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsUsed;
|
public bool IsUsed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to true to force resetting the previous mix volumes.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
|
public bool ResetPrevVolume;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reserved/padding.
|
/// Reserved/padding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe fixed byte _reserved[3];
|
private unsafe fixed byte _reserved[2];
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||||
private struct MixArray { }
|
private struct MixArray { }
|
||||||
|
@ -58,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
|
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
|
||||||
|
|
||||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||||
|
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The expected constant of any input header.
|
/// The expected constant of any input header.
|
||||||
|
|
|
@ -42,10 +42,16 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsUsed;
|
public bool IsUsed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to true to force resetting the previous mix volumes.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
|
public bool ResetPrevVolume;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reserved/padding.
|
/// Reserved/padding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe fixed byte _reserved[11];
|
private unsafe fixed byte _reserved[10];
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||||
private struct MixArray { }
|
private struct MixArray { }
|
||||||
|
@ -63,6 +69,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||||
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
|
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
|
||||||
|
|
||||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||||
|
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The expected constant of any input header.
|
/// The expected constant of any input header.
|
||||||
|
|
|
@ -108,10 +108,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// <remarks>This was added in system update 17.0.0</remarks>
|
/// <remarks>This was added in system update 17.0.0</remarks>
|
||||||
public const int Revision12 = 12 << 24;
|
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>
|
/// <summary>
|
||||||
/// Last revision supported by the implementation.
|
/// Last revision supported by the implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int LastRevision = Revision12;
|
public const int LastRevision = Revision13;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Target revision magic supported by the implementation.
|
/// Target revision magic supported by the implementation.
|
||||||
|
@ -384,6 +392,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12);
|
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>
|
/// <summary>
|
||||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -583,11 +583,20 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
/// <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)
|
||||||
{
|
{
|
||||||
if (parameter.IsChannelCountValid())
|
if (parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId);
|
CompressorCommand command = new(bufferOffset, parameter, state, effectResultState, isEnabled, nodeId);
|
||||||
|
|
||||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
|
|
|
@ -735,14 +735,26 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId)
|
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId)
|
||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.Compressor);
|
Debug.Assert(effect.Type == EffectType.Compressor);
|
||||||
|
|
||||||
|
Memory<EffectResultState> dspResultState;
|
||||||
|
|
||||||
|
if (effect.Parameter.StatisticsEnabled)
|
||||||
|
{
|
||||||
|
dspResultState = _effectContext.GetDspStateMemory(effectId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dspResultState = Memory<EffectResultState>.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
_commandBuffer.GenerateCompressorEffect(
|
_commandBuffer.GenerateCompressorEffect(
|
||||||
bufferOffset,
|
bufferOffset,
|
||||||
effect.Parameter,
|
effect.Parameter,
|
||||||
effect.State,
|
effect.State,
|
||||||
|
dspResultState,
|
||||||
effect.IsEnabled,
|
effect.IsEnabled,
|
||||||
nodeId);
|
nodeId);
|
||||||
}
|
}
|
||||||
|
@ -795,7 +807,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
||||||
break;
|
break;
|
||||||
case EffectType.Compressor:
|
case EffectType.Compressor:
|
||||||
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId);
|
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
||||||
|
|
|
@ -168,16 +168,30 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
if (command.Enabled)
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
if (command.Parameter.StatisticsEnabled)
|
||||||
{
|
{
|
||||||
return command.Parameter.ChannelCount switch
|
return command.Parameter.ChannelCount switch
|
||||||
{
|
{
|
||||||
1 => 34431,
|
1 => 22100,
|
||||||
2 => 44253,
|
2 => 33211,
|
||||||
4 => 63827,
|
4 => 41587,
|
||||||
6 => 83361,
|
6 => 58819,
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
_ => 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}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return command.Parameter.ChannelCount switch
|
return command.Parameter.ChannelCount switch
|
||||||
{
|
{
|
||||||
|
@ -190,16 +204,30 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.Enabled)
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
if (command.Parameter.StatisticsEnabled)
|
||||||
{
|
{
|
||||||
return command.Parameter.ChannelCount switch
|
return command.Parameter.ChannelCount switch
|
||||||
{
|
{
|
||||||
1 => 51095,
|
1 => 32518,
|
||||||
2 => 65693,
|
2 => 49102,
|
||||||
4 => 95383,
|
4 => 61685,
|
||||||
6 => 124510,
|
6 => 87250,
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
_ => 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}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return command.Parameter.ChannelCount switch
|
return command.Parameter.ChannelCount switch
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,6 +62,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||||
UpdateUsageStateForCommandGeneration();
|
UpdateUsageStateForCommandGeneration();
|
||||||
|
|
||||||
Parameter.Status = UsageState.Enabled;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBugFixed { get; private set; }
|
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>
|
/// <summary>
|
||||||
/// Initialize <see cref="SplitterContext"/>.
|
/// Initialize <see cref="SplitterContext"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -139,6 +144,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported();
|
||||||
|
|
||||||
SplitterState.InitializeSplitters(splitters.Span);
|
SplitterState.InitializeSplitters(splitters.Span);
|
||||||
|
|
||||||
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
||||||
|
@ -277,7 +284,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
{
|
{
|
||||||
SplitterDestination destination = GetDestination(parameter.Id);
|
SplitterDestination destination = GetDestination(parameter.Id);
|
||||||
|
|
||||||
destination.Update(parameter);
|
destination.Update(parameter, IsSplitterPrevVolumeResetSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -184,15 +184,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// Update the splitter destination data from user parameter.
|
/// Update the splitter destination data from user parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
/// <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
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
if (Unsafe.IsNullRef(ref _v2))
|
||||||
{
|
{
|
||||||
_v1.Update(parameter);
|
_v1.Update(parameter, isPrevVolumeResetSupported);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_v2.Update(parameter);
|
_v2.Update(parameter, isPrevVolumeResetSupported);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
|
/// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
/// <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);
|
Debug.Assert(Id == parameter.Id);
|
||||||
|
|
||||||
|
@ -103,7 +104,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
|
|
||||||
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||||
|
|
||||||
if (!IsUsed && parameter.IsUsed)
|
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
|
||||||
|
if (resetPrevVolume)
|
||||||
{
|
{
|
||||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
/// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
|
/// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
/// <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);
|
Debug.Assert(Id == parameter.Id);
|
||||||
|
|
||||||
|
@ -110,7 +111,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||||
|
|
||||||
_biquadFilters = parameter.BiquadFilters;
|
_biquadFilters = parameter.BiquadFilters;
|
||||||
|
|
||||||
if (!IsUsed && parameter.IsUsed)
|
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
|
||||||
|
if (resetPrevVolume)
|
||||||
{
|
{
|
||||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,33 @@
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Common.GraphicsDriver
|
namespace Ryujinx.Common.GraphicsDriver
|
||||||
{
|
{
|
||||||
public static class DriverUtilities
|
public static class DriverUtilities
|
||||||
{
|
{
|
||||||
|
private static void AddMesaFlags(string envVar, string newFlags)
|
||||||
|
{
|
||||||
|
string existingFlags = Environment.GetEnvironmentVariable(envVar);
|
||||||
|
|
||||||
|
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
|
||||||
|
|
||||||
|
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitDriverConfig(bool oglThreading)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux())
|
||||||
|
{
|
||||||
|
AddMesaFlags("RADV_DEBUG", "nodcc");
|
||||||
|
}
|
||||||
|
|
||||||
|
ToggleOGLThreading(oglThreading);
|
||||||
|
}
|
||||||
|
|
||||||
public static void ToggleOGLThreading(bool enabled)
|
public static void ToggleOGLThreading(bool enabled)
|
||||||
{
|
{
|
||||||
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
|
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
|
||||||
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
24
src/Ryujinx.Common/Utilities/OsUtils.cs
Normal file
24
src/Ryujinx.Common/Utilities/OsUtils.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,6 +71,8 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
public readonly int GatherBiasPrecision;
|
public readonly int GatherBiasPrecision;
|
||||||
|
|
||||||
|
public readonly ulong MaximumGpuMemory;
|
||||||
|
|
||||||
public Capabilities(
|
public Capabilities(
|
||||||
TargetApi api,
|
TargetApi api,
|
||||||
string vendorName,
|
string vendorName,
|
||||||
|
@ -131,7 +133,8 @@ namespace Ryujinx.Graphics.GAL
|
||||||
int shaderSubgroupSize,
|
int shaderSubgroupSize,
|
||||||
int storageBufferOffsetAlignment,
|
int storageBufferOffsetAlignment,
|
||||||
int textureBufferOffsetAlignment,
|
int textureBufferOffsetAlignment,
|
||||||
int gatherBiasPrecision)
|
int gatherBiasPrecision,
|
||||||
|
ulong maximumGpuMemory)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
VendorName = vendorName;
|
VendorName = vendorName;
|
||||||
|
@ -193,6 +196,7 @@ namespace Ryujinx.Graphics.GAL
|
||||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||||
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
||||||
GatherBiasPrecision = gatherBiasPrecision;
|
GatherBiasPrecision = gatherBiasPrecision;
|
||||||
|
MaximumGpuMemory = maximumGpuMemory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public interface IImageArray : IDisposable
|
public interface IImageArray : IDisposable
|
||||||
{
|
{
|
||||||
void SetFormats(int index, Format[] imageFormats);
|
|
||||||
void SetImages(int index, ITexture[] images);
|
void SetImages(int index, ITexture[] images);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
||||||
|
|
||||||
void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
|
void SetImage(ShaderStage stage, int binding, ITexture texture);
|
||||||
void SetImageArray(ShaderStage stage, int binding, IImageArray array);
|
void SetImageArray(ShaderStage stage, int binding, IImageArray array);
|
||||||
void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
|
void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Buffers;
|
using Ryujinx.Common.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
|
@ -18,30 +18,30 @@ namespace Ryujinx.Graphics.GAL
|
||||||
PinnedSpan<byte> GetData(int layer, int level);
|
PinnedSpan<byte> GetData(int layer, int level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
void SetData(IMemoryOwner<byte> data);
|
void SetData(MemoryOwner<byte> data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
void SetData(IMemoryOwner<byte> data, int layer, int level);
|
void SetData(MemoryOwner<byte> data, int layer, int level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
/// <param name="region">Target sub-region of the texture to update</param>
|
/// <param name="region">Target sub-region of the texture to update</param>
|
||||||
void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
|
void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
|
||||||
|
|
||||||
void SetStorage(BufferRange buffer);
|
void SetStorage(BufferRange buffer);
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
||||||
|
|
||||||
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
|
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
|
||||||
Register<ImageArraySetFormatsCommand>(CommandType.ImageArraySetFormats);
|
|
||||||
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
||||||
|
|
||||||
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
|
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
|
||||||
|
|
|
@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
CounterEventFlush,
|
CounterEventFlush,
|
||||||
|
|
||||||
ImageArrayDispose,
|
ImageArrayDispose,
|
||||||
ImageArraySetFormats,
|
|
||||||
ImageArraySetImages,
|
ImageArraySetImages,
|
||||||
|
|
||||||
ProgramDispose,
|
ProgramDispose,
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,19 +10,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||||
private ShaderStage _stage;
|
private ShaderStage _stage;
|
||||||
private int _binding;
|
private int _binding;
|
||||||
private TableRef<ITexture> _texture;
|
private TableRef<ITexture> _texture;
|
||||||
private Format _imageFormat;
|
|
||||||
|
|
||||||
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, Format imageFormat)
|
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture)
|
||||||
{
|
{
|
||||||
_stage = stage;
|
_stage = stage;
|
||||||
_binding = binding;
|
_binding = binding;
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_imageFormat = imageFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
|
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
|
@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetData;
|
public readonly CommandType CommandType => CommandType.TextureSetData;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<IMemoryOwner<byte>> _data;
|
private TableRef<MemoryOwner<byte>> _data;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
|
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<IMemoryOwner<byte>> _data;
|
private TableRef<MemoryOwner<byte>> _data;
|
||||||
private int _layer;
|
private int _layer;
|
||||||
private int _level;
|
private int _level;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
|
@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
|
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<IMemoryOwner<byte>> _data;
|
private TableRef<MemoryOwner<byte>> _data;
|
||||||
private int _layer;
|
private int _layer;
|
||||||
private int _level;
|
private int _level;
|
||||||
private Rectangle<int> _region;
|
private Rectangle<int> _region;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
|
@ -27,12 +27,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFormats(int index, Format[] imageFormats)
|
|
||||||
{
|
|
||||||
_renderer.New<ImageArraySetFormatsCommand>().Set(Ref(this), index, Ref(imageFormats));
|
|
||||||
_renderer.QueueCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetImages(int index, ITexture[] images)
|
public void SetImages(int index, ITexture[] images)
|
||||||
{
|
{
|
||||||
_renderer.New<ImageArraySetImagesCommand>().Set(Ref(this), index, Ref(images));
|
_renderer.New<ImageArraySetImagesCommand>().Set(Ref(this), index, Ref(images));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
{
|
{
|
||||||
|
@ -111,21 +111,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(IMemoryOwner<byte> data)
|
public void SetData(MemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
|
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
|
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
|
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
|
|
|
@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
public void SetImage(ShaderStage stage, int binding, ITexture texture)
|
||||||
{
|
{
|
||||||
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture), imageFormat);
|
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,5 +5,6 @@ namespace Ryujinx.Graphics.GAL
|
||||||
Bilinear,
|
Bilinear,
|
||||||
Nearest,
|
Nearest,
|
||||||
Fsr,
|
Fsr,
|
||||||
|
Area,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -276,8 +276,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
dstBaseOffset += dstStride * (yCount - 1);
|
dstBaseOffset += dstStride * (yCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
|
|
||||||
|
|
||||||
// If remapping is disabled, we always copy the components directly, in order.
|
// If remapping is disabled, we always copy the components directly, in order.
|
||||||
// If it's enabled, but the mapping is just XYZW, we also copy them in order.
|
// If it's enabled, but the mapping is just XYZW, we also copy them in order.
|
||||||
bool isIdentityRemap = !remap ||
|
bool isIdentityRemap = !remap ||
|
||||||
|
@ -289,6 +287,52 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
|
bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
|
||||||
bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
|
bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
|
||||||
|
|
||||||
|
// Check if the source texture exists on the GPU, if it does, do a GPU side copy.
|
||||||
|
// Otherwise, we would need to flush the source texture which is costly.
|
||||||
|
// We don't expect the source to be linear in such cases, as linear source usually indicates buffer or CPU written data.
|
||||||
|
|
||||||
|
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
|
||||||
|
{
|
||||||
|
var source = memoryManager.Physical.TextureCache.FindTexture(
|
||||||
|
memoryManager,
|
||||||
|
srcGpuVa,
|
||||||
|
srcBpp,
|
||||||
|
srcStride,
|
||||||
|
src.Height,
|
||||||
|
xCount,
|
||||||
|
yCount,
|
||||||
|
srcLinear,
|
||||||
|
src.MemoryLayout.UnpackGobBlocksInY(),
|
||||||
|
src.MemoryLayout.UnpackGobBlocksInZ());
|
||||||
|
|
||||||
|
if (source != null && source.Height == yCount)
|
||||||
|
{
|
||||||
|
source.SynchronizeMemory();
|
||||||
|
|
||||||
|
var target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||||
|
memoryManager,
|
||||||
|
source.Info.FormatInfo,
|
||||||
|
dstGpuVa,
|
||||||
|
xCount,
|
||||||
|
yCount,
|
||||||
|
dstStride,
|
||||||
|
dstLinear,
|
||||||
|
dst.MemoryLayout.UnpackGobBlocksInY(),
|
||||||
|
dst.MemoryLayout.UnpackGobBlocksInZ());
|
||||||
|
|
||||||
|
if (source.ScaleFactor != target.ScaleFactor)
|
||||||
|
{
|
||||||
|
target.PropagateScale(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
source.HostTexture.CopyTo(target.HostTexture, 0, 0);
|
||||||
|
target.SignalModified();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
|
||||||
|
|
||||||
// Try to set the texture data directly,
|
// Try to set the texture data directly,
|
||||||
// but only if we are doing a complete copy,
|
// but only if we are doing a complete copy,
|
||||||
// and not for block linear to linear copies, since those are typically accessed from the CPU.
|
// and not for block linear to linear copies, since those are typically accessed from the CPU.
|
||||||
|
@ -309,7 +353,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> data;
|
MemoryOwner<byte> data;
|
||||||
if (srcLinear)
|
if (srcLinear)
|
||||||
{
|
{
|
||||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
@ -61,51 +62,51 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="format">Shader image format</param>
|
/// <param name="format">Shader image format</param>
|
||||||
/// <returns>Texture format</returns>
|
/// <returns>Texture format</returns>
|
||||||
public static Format GetFormat(TextureFormat format)
|
public static FormatInfo GetFormatInfo(TextureFormat format)
|
||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE0055 // Disable formatting
|
#pragma warning disable IDE0055 // Disable formatting
|
||||||
TextureFormat.R8Unorm => Format.R8Unorm,
|
TextureFormat.R8Unorm => new(Format.R8Unorm, 1, 1, 1, 1),
|
||||||
TextureFormat.R8Snorm => Format.R8Snorm,
|
TextureFormat.R8Snorm => new(Format.R8Snorm, 1, 1, 1, 1),
|
||||||
TextureFormat.R8Uint => Format.R8Uint,
|
TextureFormat.R8Uint => new(Format.R8Uint, 1, 1, 1, 1),
|
||||||
TextureFormat.R8Sint => Format.R8Sint,
|
TextureFormat.R8Sint => new(Format.R8Sint, 1, 1, 1, 1),
|
||||||
TextureFormat.R16Float => Format.R16Float,
|
TextureFormat.R16Float => new(Format.R16Float, 1, 1, 2, 1),
|
||||||
TextureFormat.R16Unorm => Format.R16Unorm,
|
TextureFormat.R16Unorm => new(Format.R16Unorm, 1, 1, 2, 1),
|
||||||
TextureFormat.R16Snorm => Format.R16Snorm,
|
TextureFormat.R16Snorm => new(Format.R16Snorm, 1, 1, 2, 1),
|
||||||
TextureFormat.R16Uint => Format.R16Uint,
|
TextureFormat.R16Uint => new(Format.R16Uint, 1, 1, 2, 1),
|
||||||
TextureFormat.R16Sint => Format.R16Sint,
|
TextureFormat.R16Sint => new(Format.R16Sint, 1, 1, 2, 1),
|
||||||
TextureFormat.R32Float => Format.R32Float,
|
TextureFormat.R32Float => new(Format.R32Float, 1, 1, 4, 1),
|
||||||
TextureFormat.R32Uint => Format.R32Uint,
|
TextureFormat.R32Uint => new(Format.R32Uint, 1, 1, 4, 1),
|
||||||
TextureFormat.R32Sint => Format.R32Sint,
|
TextureFormat.R32Sint => new(Format.R32Sint, 1, 1, 4, 1),
|
||||||
TextureFormat.R8G8Unorm => Format.R8G8Unorm,
|
TextureFormat.R8G8Unorm => new(Format.R8G8Unorm, 1, 1, 2, 2),
|
||||||
TextureFormat.R8G8Snorm => Format.R8G8Snorm,
|
TextureFormat.R8G8Snorm => new(Format.R8G8Snorm, 1, 1, 2, 2),
|
||||||
TextureFormat.R8G8Uint => Format.R8G8Uint,
|
TextureFormat.R8G8Uint => new(Format.R8G8Uint, 1, 1, 2, 2),
|
||||||
TextureFormat.R8G8Sint => Format.R8G8Sint,
|
TextureFormat.R8G8Sint => new(Format.R8G8Sint, 1, 1, 2, 2),
|
||||||
TextureFormat.R16G16Float => Format.R16G16Float,
|
TextureFormat.R16G16Float => new(Format.R16G16Float, 1, 1, 4, 2),
|
||||||
TextureFormat.R16G16Unorm => Format.R16G16Unorm,
|
TextureFormat.R16G16Unorm => new(Format.R16G16Unorm, 1, 1, 4, 2),
|
||||||
TextureFormat.R16G16Snorm => Format.R16G16Snorm,
|
TextureFormat.R16G16Snorm => new(Format.R16G16Snorm, 1, 1, 4, 2),
|
||||||
TextureFormat.R16G16Uint => Format.R16G16Uint,
|
TextureFormat.R16G16Uint => new(Format.R16G16Uint, 1, 1, 4, 2),
|
||||||
TextureFormat.R16G16Sint => Format.R16G16Sint,
|
TextureFormat.R16G16Sint => new(Format.R16G16Sint, 1, 1, 4, 2),
|
||||||
TextureFormat.R32G32Float => Format.R32G32Float,
|
TextureFormat.R32G32Float => new(Format.R32G32Float, 1, 1, 8, 2),
|
||||||
TextureFormat.R32G32Uint => Format.R32G32Uint,
|
TextureFormat.R32G32Uint => new(Format.R32G32Uint, 1, 1, 8, 2),
|
||||||
TextureFormat.R32G32Sint => Format.R32G32Sint,
|
TextureFormat.R32G32Sint => new(Format.R32G32Sint, 1, 1, 8, 2),
|
||||||
TextureFormat.R8G8B8A8Unorm => Format.R8G8B8A8Unorm,
|
TextureFormat.R8G8B8A8Unorm => new(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
|
||||||
TextureFormat.R8G8B8A8Snorm => Format.R8G8B8A8Snorm,
|
TextureFormat.R8G8B8A8Snorm => new(Format.R8G8B8A8Snorm, 1, 1, 4, 4),
|
||||||
TextureFormat.R8G8B8A8Uint => Format.R8G8B8A8Uint,
|
TextureFormat.R8G8B8A8Uint => new(Format.R8G8B8A8Uint, 1, 1, 4, 4),
|
||||||
TextureFormat.R8G8B8A8Sint => Format.R8G8B8A8Sint,
|
TextureFormat.R8G8B8A8Sint => new(Format.R8G8B8A8Sint, 1, 1, 4, 4),
|
||||||
TextureFormat.R16G16B16A16Float => Format.R16G16B16A16Float,
|
TextureFormat.R16G16B16A16Float => new(Format.R16G16B16A16Float, 1, 1, 8, 4),
|
||||||
TextureFormat.R16G16B16A16Unorm => Format.R16G16B16A16Unorm,
|
TextureFormat.R16G16B16A16Unorm => new(Format.R16G16B16A16Unorm, 1, 1, 8, 4),
|
||||||
TextureFormat.R16G16B16A16Snorm => Format.R16G16B16A16Snorm,
|
TextureFormat.R16G16B16A16Snorm => new(Format.R16G16B16A16Snorm, 1, 1, 8, 4),
|
||||||
TextureFormat.R16G16B16A16Uint => Format.R16G16B16A16Uint,
|
TextureFormat.R16G16B16A16Uint => new(Format.R16G16B16A16Uint, 1, 1, 8, 4),
|
||||||
TextureFormat.R16G16B16A16Sint => Format.R16G16B16A16Sint,
|
TextureFormat.R16G16B16A16Sint => new(Format.R16G16B16A16Sint, 1, 1, 8, 4),
|
||||||
TextureFormat.R32G32B32A32Float => Format.R32G32B32A32Float,
|
TextureFormat.R32G32B32A32Float => new(Format.R32G32B32A32Float, 1, 1, 16, 4),
|
||||||
TextureFormat.R32G32B32A32Uint => Format.R32G32B32A32Uint,
|
TextureFormat.R32G32B32A32Uint => new(Format.R32G32B32A32Uint, 1, 1, 16, 4),
|
||||||
TextureFormat.R32G32B32A32Sint => Format.R32G32B32A32Sint,
|
TextureFormat.R32G32B32A32Sint => new(Format.R32G32B32A32Sint, 1, 1, 16, 4),
|
||||||
TextureFormat.R10G10B10A2Unorm => Format.R10G10B10A2Unorm,
|
TextureFormat.R10G10B10A2Unorm => new(Format.R10G10B10A2Unorm, 1, 1, 4, 4),
|
||||||
TextureFormat.R10G10B10A2Uint => Format.R10G10B10A2Uint,
|
TextureFormat.R10G10B10A2Uint => new(Format.R10G10B10A2Uint, 1, 1, 4, 4),
|
||||||
TextureFormat.R11G11B10Float => Format.R11G11B10Float,
|
TextureFormat.R11G11B10Float => new(Format.R11G11B10Float, 1, 1, 4, 3),
|
||||||
_ => 0,
|
_ => FormatInfo.Invalid,
|
||||||
#pragma warning restore IDE0055
|
#pragma warning restore IDE0055
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,7 +415,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
#pragma warning disable CS0649 // Field is never assigned to
|
#pragma warning disable CS0649 // Field is never assigned to
|
||||||
public int Width;
|
public int Width;
|
||||||
public int Height;
|
public int Height;
|
||||||
public int Depth;
|
public ushort Depth;
|
||||||
|
public ushort Flags;
|
||||||
|
|
||||||
|
public readonly bool UnpackIsLayered()
|
||||||
|
{
|
||||||
|
return (Flags & 1) == 0;
|
||||||
|
}
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -46,7 +47,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
private const int MinCountForDeletion = 32;
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
private const ulong MaxTextureSizeCapacity = 1024 * 1024 * 1024; // MB;
|
private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024;
|
||||||
|
private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024;
|
||||||
|
private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024;
|
||||||
|
private const float MemoryScaleFactor = 0.50f;
|
||||||
|
private ulong _maxCacheMemoryUsage = 0;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private ulong _totalSize;
|
private ulong _totalSize;
|
||||||
|
@ -56,6 +61,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
private readonly Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
|
private readonly Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="context">The GPU context that the cache belongs to</param>
|
||||||
|
public void Initialize(GpuContext context)
|
||||||
|
{
|
||||||
|
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
|
||||||
|
|
||||||
|
_maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity);
|
||||||
|
|
||||||
|
if (context.Capabilities.MaximumGpuMemory == 0)
|
||||||
|
{
|
||||||
|
_maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the automatic deletion cache.
|
/// Creates a new instance of the automatic deletion cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -85,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
if (_textures.Count > MaxCapacity ||
|
if (_textures.Count > MaxCapacity ||
|
||||||
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
(_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion))
|
||||||
{
|
{
|
||||||
RemoveLeastUsedTexture();
|
RemoveLeastUsedTexture();
|
||||||
}
|
}
|
||||||
|
@ -110,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
_textures.AddLast(texture.CacheNode);
|
_textures.AddLast(texture.CacheNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
if (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion)
|
||||||
{
|
{
|
||||||
RemoveLeastUsedTexture();
|
RemoveLeastUsedTexture();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
readonly struct FormatInfo
|
readonly struct FormatInfo
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An invalid texture format.
|
||||||
|
/// </summary>
|
||||||
|
public static FormatInfo Invalid { get; } = new(0, 0, 0, 0, 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A default, generic RGBA8 texture format.
|
/// A default, generic RGBA8 texture format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -23,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Must be 1 for non-compressed formats.
|
/// Must be 1 for non-compressed formats.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int BlockWidth { get; }
|
public byte BlockWidth { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The block height for compressed formats.
|
/// The block height for compressed formats.
|
||||||
|
@ -31,17 +36,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Must be 1 for non-compressed formats.
|
/// Must be 1 for non-compressed formats.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int BlockHeight { get; }
|
public byte BlockHeight { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of bytes occupied by a single pixel in memory of the texture data.
|
/// The number of bytes occupied by a single pixel in memory of the texture data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int BytesPerPixel { get; }
|
public byte BytesPerPixel { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum number of components this format has defined (in RGBA order).
|
/// The maximum number of components this format has defined (in RGBA order).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Components { get; }
|
public byte Components { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whenever or not the texture format is a compressed format. Determined from block size.
|
/// Whenever or not the texture format is a compressed format. Determined from block size.
|
||||||
|
@ -57,10 +62,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
|
/// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
|
||||||
public FormatInfo(
|
public FormatInfo(
|
||||||
Format format,
|
Format format,
|
||||||
int blockWidth,
|
byte blockWidth,
|
||||||
int blockHeight,
|
byte blockHeight,
|
||||||
int bytesPerPixel,
|
byte bytesPerPixel,
|
||||||
int components)
|
byte components)
|
||||||
{
|
{
|
||||||
Format = format;
|
Format = format;
|
||||||
BlockWidth = blockWidth;
|
BlockWidth = blockWidth;
|
||||||
|
|
|
@ -13,6 +13,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if the sampler has sRGB conversion enabled, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSrgb { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Host sampler object.
|
/// Host sampler object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -30,6 +35,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="descriptor">The Maxwell sampler descriptor</param>
|
/// <param name="descriptor">The Maxwell sampler descriptor</param>
|
||||||
public Sampler(GpuContext context, SamplerDescriptor descriptor)
|
public Sampler(GpuContext context, SamplerDescriptor descriptor)
|
||||||
{
|
{
|
||||||
|
IsSrgb = descriptor.UnpackSrgb();
|
||||||
|
|
||||||
MinFilter minFilter = descriptor.UnpackMinFilter();
|
MinFilter minFilter = descriptor.UnpackMinFilter();
|
||||||
MagFilter magFilter = descriptor.UnpackMagFilter();
|
MagFilter magFilter = descriptor.UnpackMagFilter();
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks the sampler sRGB format flag.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the has sampler is sRGB conversion enabled, false otherwise</returns>
|
||||||
|
public readonly bool UnpackSrgb()
|
||||||
|
{
|
||||||
|
return (Word0 & (1 << 13)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering.
|
/// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -7,7 +7,6 @@ using Ryujinx.Graphics.Texture.Astc;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -662,7 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> result = ConvertToHostCompatibleFormat(data);
|
MemoryOwner<byte> result = ConvertToHostCompatibleFormat(data);
|
||||||
|
|
||||||
if (ScaleFactor != 1f && AllowScaledSetData())
|
if (ScaleFactor != 1f && AllowScaledSetData())
|
||||||
{
|
{
|
||||||
|
@ -685,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// Uploads new texture data to the host GPU.
|
/// Uploads new texture data to the host GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">New data</param>
|
/// <param name="data">New data</param>
|
||||||
public void SetData(IMemoryOwner<byte> data)
|
public void SetData(MemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
|
@ -704,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="data">New data</param>
|
/// <param name="data">New data</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
|
@ -722,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
/// <param name="region">Target sub-region of the texture to update</param>
|
/// <param name="region">Target sub-region of the texture to update</param>
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
|
@ -740,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="level">Mip level to convert</param>
|
/// <param name="level">Mip level to convert</param>
|
||||||
/// <param name="single">True to convert a single slice</param>
|
/// <param name="single">True to convert a single slice</param>
|
||||||
/// <returns>Converted data</returns>
|
/// <returns>Converted data</returns>
|
||||||
public IMemoryOwner<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
public MemoryOwner<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||||
{
|
{
|
||||||
int width = Info.Width;
|
int width = Info.Width;
|
||||||
int height = Info.Height;
|
int height = Info.Height;
|
||||||
|
@ -755,7 +754,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
int sliceDepth = single ? 1 : depth;
|
int sliceDepth = single ? 1 : depth;
|
||||||
|
|
||||||
IMemoryOwner<byte> linear;
|
MemoryOwner<byte> linear;
|
||||||
|
|
||||||
if (Info.IsLinear)
|
if (Info.IsLinear)
|
||||||
{
|
{
|
||||||
|
@ -788,7 +787,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> result = linear;
|
MemoryOwner<byte> result = linear;
|
||||||
|
|
||||||
// Handle compressed cases not supported by the host:
|
// Handle compressed cases not supported by the host:
|
||||||
// - ASTC is usually not supported on desktop cards.
|
// - ASTC is usually not supported on desktop cards.
|
||||||
|
@ -832,19 +831,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
case Format.Etc2RgbaUnorm:
|
case Format.Etc2RgbaUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return ETC2Decoder.DecodeRgba(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return ETC2Decoder.DecodeRgba(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Etc2RgbPtaSrgb:
|
case Format.Etc2RgbPtaSrgb:
|
||||||
case Format.Etc2RgbPtaUnorm:
|
case Format.Etc2RgbPtaUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return ETC2Decoder.DecodePta(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return ETC2Decoder.DecodePta(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Etc2RgbSrgb:
|
case Format.Etc2RgbSrgb:
|
||||||
case Format.Etc2RgbUnorm:
|
case Format.Etc2RgbUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return ETC2Decoder.DecodeRgb(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return ETC2Decoder.DecodeRgb(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -856,43 +855,43 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
case Format.Bc1RgbaUnorm:
|
case Format.Bc1RgbaUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC1(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC1(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Bc2Srgb:
|
case Format.Bc2Srgb:
|
||||||
case Format.Bc2Unorm:
|
case Format.Bc2Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC2(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC2(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Bc3Srgb:
|
case Format.Bc3Srgb:
|
||||||
case Format.Bc3Unorm:
|
case Format.Bc3Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC3(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC3(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Bc4Snorm:
|
case Format.Bc4Snorm:
|
||||||
case Format.Bc4Unorm:
|
case Format.Bc4Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC4(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
|
return BCnDecoder.DecodeBC4(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
|
||||||
}
|
}
|
||||||
case Format.Bc5Snorm:
|
case Format.Bc5Snorm:
|
||||||
case Format.Bc5Unorm:
|
case Format.Bc5Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC5(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
|
return BCnDecoder.DecodeBC5(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
|
||||||
}
|
}
|
||||||
case Format.Bc6HSfloat:
|
case Format.Bc6HSfloat:
|
||||||
case Format.Bc6HUfloat:
|
case Format.Bc6HUfloat:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC6(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
|
return BCnDecoder.DecodeBC6(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
|
||||||
}
|
}
|
||||||
case Format.Bc7Srgb:
|
case Format.Bc7Srgb:
|
||||||
case Format.Bc7Unorm:
|
case Format.Bc7Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC7(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC7(result.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,7 +899,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Memory.Span, width);
|
var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Span, width);
|
||||||
|
|
||||||
if (_context.Capabilities.SupportsR4G4B4A4Format)
|
if (_context.Capabilities.SupportsR4G4B4A4Format)
|
||||||
{
|
{
|
||||||
|
@ -910,7 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
using (converted)
|
using (converted)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Memory.Span, width);
|
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Span, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -921,7 +920,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
|
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -933,24 +932,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
case Format.R5G6B5Unorm:
|
case Format.R5G6B5Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Memory.Span, width);
|
return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Span, width);
|
||||||
}
|
}
|
||||||
case Format.B5G5R5A1Unorm:
|
case Format.B5G5R5A1Unorm:
|
||||||
case Format.R5G5B5X1Unorm:
|
case Format.R5G5B5X1Unorm:
|
||||||
case Format.R5G5B5A1Unorm:
|
case Format.R5G5B5A1Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Memory.Span, width, Format == Format.R5G5B5X1Unorm);
|
return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Span, width, Format == Format.R5G5B5X1Unorm);
|
||||||
}
|
}
|
||||||
case Format.A1B5G5R5Unorm:
|
case Format.A1B5G5R5Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Memory.Span, width);
|
return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Span, width);
|
||||||
}
|
}
|
||||||
case Format.R4G4B4A4Unorm:
|
case Format.R4G4B4A4Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
|
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For images, indicates the format specified on the shader.
|
/// For images, indicates the format specified on the shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Format Format { get; }
|
public FormatInfo FormatInfo { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shader texture host set index.
|
/// Shader texture host set index.
|
||||||
|
@ -58,17 +58,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// Constructs the texture binding information structure.
|
/// Constructs the texture binding information structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target">The shader sampler target type</param>
|
/// <param name="target">The shader sampler target type</param>
|
||||||
/// <param name="format">Format of the image as declared on the shader</param>
|
/// <param name="formatInfo">Format of the image as declared on the shader</param>
|
||||||
/// <param name="set">Shader texture host set index</param>
|
/// <param name="set">Shader texture host set index</param>
|
||||||
/// <param name="binding">The shader texture binding point</param>
|
/// <param name="binding">The shader texture binding point</param>
|
||||||
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
|
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
|
||||||
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
|
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
|
||||||
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
|
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
|
||||||
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
|
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
|
||||||
public TextureBindingInfo(Target target, Format format, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
|
public TextureBindingInfo(Target target, FormatInfo formatInfo, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
|
||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
Format = format;
|
FormatInfo = formatInfo;
|
||||||
Set = set;
|
Set = set;
|
||||||
Binding = binding;
|
Binding = binding;
|
||||||
ArrayLength = arrayLength;
|
ArrayLength = arrayLength;
|
||||||
|
@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
int cbufSlot,
|
int cbufSlot,
|
||||||
int handle,
|
int handle,
|
||||||
TextureUsageFlags flags,
|
TextureUsageFlags flags,
|
||||||
bool isSamplerOnly) : this(target, 0, set, binding, arrayLength, cbufSlot, handle, flags)
|
bool isSamplerOnly) : this(target, FormatInfo.Invalid, set, binding, arrayLength, cbufSlot, handle, flags)
|
||||||
{
|
{
|
||||||
IsSamplerOnly = isSamplerOnly;
|
IsSamplerOnly = isSamplerOnly;
|
||||||
}
|
}
|
||||||
|
|
|
@ -659,7 +659,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
|
int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
|
||||||
length = Math.Min(length, bindingInfo.ArrayLength);
|
length = Math.Min(length, bindingInfo.ArrayLength);
|
||||||
|
|
||||||
Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
|
|
||||||
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
||||||
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
||||||
|
|
||||||
|
@ -674,7 +673,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
|
@ -697,8 +696,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
|
||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
|
@ -706,26 +703,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
if (format == 0 && texture != null)
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
||||||
{
|
|
||||||
format = texture.Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isImage)
|
else if (isImage)
|
||||||
{
|
{
|
||||||
if (format == 0 && texture != null)
|
|
||||||
{
|
|
||||||
format = texture.Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
formats[index] = format;
|
|
||||||
textures[index] = hostTexture;
|
textures[index] = hostTexture;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -737,7 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
entry.ImageArray.SetFormats(0, formats);
|
|
||||||
entry.ImageArray.SetImages(0, textures);
|
entry.ImageArray.SetImages(0, textures);
|
||||||
|
|
||||||
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
||||||
|
@ -863,7 +848,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
entry.UpdateData(cachedTextureBuffer, cachedSamplerBuffer, separateSamplerBuffer);
|
entry.UpdateData(cachedTextureBuffer, cachedSamplerBuffer, separateSamplerBuffer);
|
||||||
|
|
||||||
Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
|
|
||||||
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
||||||
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
||||||
|
|
||||||
|
@ -883,7 +867,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture);
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
|
@ -916,8 +900,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
hostSampler = sampler?.GetHostSampler(texture);
|
hostSampler = sampler?.GetHostSampler(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
|
||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
|
@ -925,26 +907,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
if (format == 0 && texture != null)
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
||||||
{
|
|
||||||
format = texture.Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isImage)
|
else if (isImage)
|
||||||
{
|
{
|
||||||
if (format == 0 && texture != null)
|
|
||||||
{
|
|
||||||
format = texture.Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
formats[index] = format;
|
|
||||||
textures[index] = hostTexture;
|
textures[index] = hostTexture;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -956,7 +927,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
entry.ImageArray.SetFormats(0, formats);
|
|
||||||
entry.ImageArray.SetImages(0, textures);
|
entry.ImageArray.SetImages(0, textures);
|
||||||
|
|
||||||
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
||||||
|
|
|
@ -187,7 +187,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
(TexturePool texturePool, SamplerPool samplerPool) = GetPools();
|
(TexturePool texturePool, SamplerPool samplerPool) = GetPools();
|
||||||
|
|
||||||
return (texturePool.Get(textureId), samplerPool.Get(samplerId));
|
Sampler sampler = samplerPool?.Get(samplerId);
|
||||||
|
|
||||||
|
return (texturePool.Get(textureId, sampler?.IsSrgb ?? true), sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -508,12 +510,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
state.TextureHandle = textureId;
|
state.TextureHandle = textureId;
|
||||||
state.SamplerHandle = samplerId;
|
state.SamplerHandle = samplerId;
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
|
Sampler sampler = samplerPool?.Get(samplerId);
|
||||||
|
|
||||||
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, sampler?.IsSrgb ?? true, out Texture texture);
|
||||||
|
|
||||||
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
||||||
|
|
||||||
Sampler sampler = samplerPool?.Get(samplerId);
|
|
||||||
|
|
||||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||||
|
|
||||||
|
@ -522,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, bindingInfo.Format, false);
|
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
|
||||||
|
|
||||||
// Cache is not used for buffer texture, it must always rebind.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
|
@ -616,6 +618,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (!poolModified &&
|
if (!poolModified &&
|
||||||
state.TextureHandle == textureId &&
|
state.TextureHandle == textureId &&
|
||||||
|
state.ImageFormat == bindingInfo.FormatInfo.Format &&
|
||||||
state.CachedTexture != null &&
|
state.CachedTexture != null &&
|
||||||
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
|
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
|
||||||
{
|
{
|
||||||
|
@ -629,26 +632,22 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
cachedTexture.SignalModified();
|
cachedTexture.SignalModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
|
||||||
|
|
||||||
if (state.ImageFormat != format ||
|
|
||||||
((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
|
||||||
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage)))
|
|
||||||
{
|
{
|
||||||
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||||
|
|
||||||
state.Texture = hostTextureRebind;
|
state.Texture = hostTextureRebind;
|
||||||
state.ImageFormat = format;
|
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
|
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.TextureHandle = textureId;
|
state.TextureHandle = textureId;
|
||||||
|
state.ImageFormat = bindingInfo.FormatInfo.Format;
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
|
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture);
|
||||||
|
|
||||||
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
||||||
|
|
||||||
|
@ -660,14 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
|
||||||
|
|
||||||
if (format == 0 && texture != null)
|
|
||||||
{
|
|
||||||
format = texture.Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, format, true);
|
|
||||||
|
|
||||||
// Cache is not used for buffer texture, it must always rebind.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
|
@ -689,16 +681,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
state.Texture = hostTexture;
|
state.Texture = hostTexture;
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture);
|
||||||
|
|
||||||
if (format == 0 && texture != null)
|
|
||||||
{
|
|
||||||
format = texture.Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.ImageFormat = format;
|
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.CachedTexture = texture;
|
state.CachedTexture = texture;
|
||||||
|
|
|
@ -68,6 +68,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
_cache = new AutoDeleteCache();
|
_cache = new AutoDeleteCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
||||||
|
/// </summary>
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_cache.Initialize(_context);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles marking of textures written to a memory region being (partially) remapped.
|
/// Handles marking of textures written to a memory region being (partially) remapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -347,6 +355,53 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
|
/// <param name="formatInfo">Format of the texture</param>
|
||||||
|
/// <param name="gpuAddress">GPU virtual address of the texture</param>
|
||||||
|
/// <param name="xCount">Texture width in bytes</param>
|
||||||
|
/// <param name="yCount">Texture height</param>
|
||||||
|
/// <param name="stride">Texture stride if linear, otherwise ignored</param>
|
||||||
|
/// <param name="isLinear">Indicates if the texture is linear or block linear</param>
|
||||||
|
/// <param name="gobBlocksInY">GOB blocks in Y for block linear textures</param>
|
||||||
|
/// <param name="gobBlocksInZ">GOB blocks in Z for 3D block linear textures</param>
|
||||||
|
/// <returns>The texture</returns>
|
||||||
|
public Texture FindOrCreateTexture(
|
||||||
|
MemoryManager memoryManager,
|
||||||
|
FormatInfo formatInfo,
|
||||||
|
ulong gpuAddress,
|
||||||
|
int xCount,
|
||||||
|
int yCount,
|
||||||
|
int stride,
|
||||||
|
bool isLinear,
|
||||||
|
int gobBlocksInY,
|
||||||
|
int gobBlocksInZ)
|
||||||
|
{
|
||||||
|
TextureInfo info = new(
|
||||||
|
gpuAddress,
|
||||||
|
xCount / formatInfo.BytesPerPixel,
|
||||||
|
yCount,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
stride,
|
||||||
|
isLinear,
|
||||||
|
gobBlocksInY,
|
||||||
|
gobBlocksInZ,
|
||||||
|
1,
|
||||||
|
Target.Texture2D,
|
||||||
|
formatInfo);
|
||||||
|
|
||||||
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.ForCopy, info, 0, sizeHint: new Size(xCount, yCount, 1));
|
||||||
|
|
||||||
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -468,13 +523,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
|
layered &= size.UnpackIsLayered();
|
||||||
|
|
||||||
Target target;
|
Target target;
|
||||||
|
|
||||||
if (dsState.MemoryLayout.UnpackIsTarget3D())
|
if ((samplesInX | samplesInY) != 1)
|
||||||
{
|
|
||||||
target = Target.Texture3D;
|
|
||||||
}
|
|
||||||
else if ((samplesInX | samplesInY) != 1)
|
|
||||||
{
|
{
|
||||||
target = size.Depth > 1 && layered
|
target = size.Depth > 1 && layered
|
||||||
? Target.Texture2DMultisampleArray
|
? Target.Texture2DMultisampleArray
|
||||||
|
|
|
@ -739,7 +739,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
|
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
|
||||||
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm);
|
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm) ||
|
||||||
|
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R32Uint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
|
@ -5,7 +6,6 @@ using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
|
ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
|
||||||
|
|
||||||
IMemoryOwner<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
|
MemoryOwner<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
|
||||||
|
|
||||||
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
|
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,76 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
|
private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
|
||||||
private TextureDescriptor _defaultDescriptor;
|
private TextureDescriptor _defaultDescriptor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of textures that shares the same memory region, but have different formats.
|
||||||
|
/// </summary>
|
||||||
|
private class TextureAliasList
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Alias texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Format">Texture format</param>
|
||||||
|
/// <param name="Texture">Texture</param>
|
||||||
|
private readonly record struct Alias(Format Format, Texture Texture);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of texture aliases.
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<Alias> _aliases;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the texture alias list.
|
||||||
|
/// </summary>
|
||||||
|
public TextureAliasList()
|
||||||
|
{
|
||||||
|
_aliases = new List<Alias>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new texture alias.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">Alias format</param>
|
||||||
|
/// <param name="texture">Alias texture</param>
|
||||||
|
public void Add(Format format, Texture texture)
|
||||||
|
{
|
||||||
|
_aliases.Add(new Alias(format, texture));
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a texture with the requested format, or returns null if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">Format to find</param>
|
||||||
|
/// <returns>Texture with the requested format, or null if not found</returns>
|
||||||
|
public Texture Find(Format format)
|
||||||
|
{
|
||||||
|
foreach (var alias in _aliases)
|
||||||
|
{
|
||||||
|
if (alias.Format == format)
|
||||||
|
{
|
||||||
|
return alias.Texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all alias textures.
|
||||||
|
/// </summary>
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
foreach (var entry in _aliases)
|
||||||
|
{
|
||||||
|
entry.Texture.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
_aliases.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Dictionary<Texture, TextureAliasList> _aliasLists;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Linked list node used on the texture pool cache.
|
/// Linked list node used on the texture pool cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -95,6 +165,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -115,14 +186,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
|
||||||
|
|
||||||
// The dereference queue can put our texture back on the cache.
|
// The dereference queue can put our texture back on the cache.
|
||||||
if ((texture = ProcessDereferenceQueue(id)) != null)
|
if ((texture = ProcessDereferenceQueue(id)) != null)
|
||||||
{
|
{
|
||||||
return ref descriptor;
|
return ref descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
|
@ -157,6 +227,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
/// <returns>The texture with the given ID</returns>
|
/// <returns>The texture with the given ID</returns>
|
||||||
public override Texture Get(int id)
|
public override Texture Get(int id)
|
||||||
|
{
|
||||||
|
return Get(id, srgbSampler: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the texture with the given ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
|
/// <param name="srgbSampler">Whether the texture is being accessed with a sampler that has sRGB conversion enabled</param>
|
||||||
|
/// <returns>The texture with the given ID</returns>
|
||||||
|
public Texture Get(int id, bool srgbSampler)
|
||||||
{
|
{
|
||||||
if ((uint)id >= Items.Length)
|
if ((uint)id >= Items.Length)
|
||||||
{
|
{
|
||||||
|
@ -170,7 +251,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
SynchronizeMemory();
|
SynchronizeMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
GetInternal(id, out Texture texture);
|
GetForBinding(id, srgbSampler, out Texture texture);
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
@ -182,9 +263,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// This method assumes that the pool has been manually synchronized before doing binding.
|
/// This method assumes that the pool has been manually synchronized before doing binding.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
|
/// <param name="srgbSampler">Whether the texture is being accessed with a sampler that has sRGB conversion enabled</param>
|
||||||
/// <param name="texture">The texture with the given ID</param>
|
/// <param name="texture">The texture with the given ID</param>
|
||||||
/// <returns>The texture descriptor with the given ID</returns>
|
/// <returns>The texture descriptor with the given ID</returns>
|
||||||
public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture)
|
public ref readonly TextureDescriptor GetForBinding(int id, bool srgbSampler, out Texture texture)
|
||||||
{
|
{
|
||||||
if ((uint)id >= Items.Length)
|
if ((uint)id >= Items.Length)
|
||||||
{
|
{
|
||||||
|
@ -194,9 +276,66 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
// When getting for binding, assume the pool has already been synchronized.
|
// When getting for binding, assume the pool has already been synchronized.
|
||||||
|
|
||||||
|
if (!srgbSampler)
|
||||||
|
{
|
||||||
|
// If the sampler does not have the sRGB bit enabled, then the texture can't use a sRGB format.
|
||||||
|
ref readonly TextureDescriptor tempDescriptor = ref GetDescriptorRef(id);
|
||||||
|
|
||||||
|
if (tempDescriptor.UnpackSrgb() && FormatTable.TryGetTextureFormat(tempDescriptor.UnpackFormat(), isSrgb: false, out FormatInfo formatInfo))
|
||||||
|
{
|
||||||
|
// Get a view of the texture with the right format.
|
||||||
|
return ref GetForBinding(id, formatInfo, out texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ref GetInternal(id, out texture);
|
return ref GetInternal(id, out texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the texture descriptor and texture with the given ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method assumes that the pool has been manually synchronized before doing binding.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
|
/// <param name="formatInfo">Texture format information</param>
|
||||||
|
/// <param name="texture">The texture with the given ID</param>
|
||||||
|
/// <returns>The texture descriptor with the given ID</returns>
|
||||||
|
public ref readonly TextureDescriptor GetForBinding(int id, FormatInfo formatInfo, out Texture texture)
|
||||||
|
{
|
||||||
|
if ((uint)id >= Items.Length)
|
||||||
|
{
|
||||||
|
texture = null;
|
||||||
|
return ref _defaultDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref readonly TextureDescriptor descriptor = ref GetInternal(id, out texture);
|
||||||
|
|
||||||
|
if (texture != null && formatInfo.Format != 0 && texture.Format != formatInfo.Format)
|
||||||
|
{
|
||||||
|
if (!_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
|
||||||
|
{
|
||||||
|
_aliasLists.Add(texture, aliasList = new TextureAliasList());
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = aliasList.Find(formatInfo.Format);
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
|
info = ChangeFormat(info, formatInfo);
|
||||||
|
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
aliasList.Add(formatInfo.Format, texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -234,6 +373,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount();
|
texture.DecrementReferenceCount();
|
||||||
|
RemoveAliasList(texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +467,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount();
|
texture.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveAliasList(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -369,6 +511,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
if (Interlocked.Exchange(ref Items[id], null) != null)
|
if (Interlocked.Exchange(ref Items[id], null) != null)
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount(this, id);
|
texture.DecrementReferenceCount(this, id);
|
||||||
|
RemoveAliasList(texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,6 +765,57 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
component == SwizzleComponent.Green;
|
component == SwizzleComponent.Green;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the format on the texture information structure, and also adjusts the width for the new format if needed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">Texture information</param>
|
||||||
|
/// <param name="dstFormat">New format</param>
|
||||||
|
/// <returns>Texture information with the new format</returns>
|
||||||
|
private static TextureInfo ChangeFormat(in TextureInfo info, FormatInfo dstFormat)
|
||||||
|
{
|
||||||
|
int width = info.Width;
|
||||||
|
|
||||||
|
if (info.FormatInfo.BytesPerPixel != dstFormat.BytesPerPixel)
|
||||||
|
{
|
||||||
|
int stride = width * info.FormatInfo.BytesPerPixel;
|
||||||
|
width = stride / dstFormat.BytesPerPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureInfo(
|
||||||
|
info.GpuAddress,
|
||||||
|
width,
|
||||||
|
info.Height,
|
||||||
|
info.DepthOrLayers,
|
||||||
|
info.Levels,
|
||||||
|
info.SamplesInX,
|
||||||
|
info.SamplesInY,
|
||||||
|
info.Stride,
|
||||||
|
info.IsLinear,
|
||||||
|
info.GobBlocksInY,
|
||||||
|
info.GobBlocksInZ,
|
||||||
|
info.GobBlocksInTileX,
|
||||||
|
info.Target,
|
||||||
|
dstFormat,
|
||||||
|
info.DepthStencilMode,
|
||||||
|
info.SwizzleR,
|
||||||
|
info.SwizzleG,
|
||||||
|
info.SwizzleB,
|
||||||
|
info.SwizzleA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all aliases for a texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">Texture to have the aliases removed</param>
|
||||||
|
private void RemoveAliasList(Texture texture)
|
||||||
|
{
|
||||||
|
if (_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
|
||||||
|
{
|
||||||
|
_aliasLists.Remove(texture);
|
||||||
|
aliasList.Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Decrements the reference count of the texture.
|
/// Decrements the reference count of the texture.
|
||||||
/// This indicates that the texture pool is not using it anymore.
|
/// This indicates that the texture pool is not using it anymore.
|
||||||
|
@ -629,7 +823,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="item">The texture to be deleted</param>
|
/// <param name="item">The texture to be deleted</param>
|
||||||
protected override void Delete(Texture item)
|
protected override void Delete(Texture item)
|
||||||
{
|
{
|
||||||
item?.DecrementReferenceCount(this);
|
if (item != null)
|
||||||
|
{
|
||||||
|
item.DecrementReferenceCount(this);
|
||||||
|
RemoveAliasList(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
|
|
|
@ -509,7 +509,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
if (binding.IsImage)
|
if (binding.IsImage)
|
||||||
{
|
{
|
||||||
_context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format);
|
_context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -873,12 +873,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
Format format,
|
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage));
|
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -897,12 +896,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index,
|
int index)
|
||||||
Format format)
|
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index, format));
|
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -921,12 +919,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index,
|
int index)
|
||||||
Format format)
|
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index, format));
|
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -34,33 +34,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The image format for the binding.
|
|
||||||
/// </summary>
|
|
||||||
public Format Format { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new buffer texture binding.
|
/// Create a new buffer texture binding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="array">Array</param>
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="index">Index of the binding on the array</param>
|
/// <param name="index">Index of the binding on the array</param>
|
||||||
/// <param name="format">Binding format</param>
|
|
||||||
public BufferTextureArrayBinding(
|
public BufferTextureArrayBinding(
|
||||||
T array,
|
T array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index,
|
int index)
|
||||||
Format format)
|
|
||||||
{
|
{
|
||||||
Array = array;
|
Array = array;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
Index = index;
|
Index = index;
|
||||||
Format = format;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureBindingInfo BindingInfo { get; }
|
public TextureBindingInfo BindingInfo { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The image format for the binding.
|
|
||||||
/// </summary>
|
|
||||||
public Format Format { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the binding is for an image or a sampler.
|
/// Whether the binding is for an image or a sampler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -47,21 +42,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="format">Binding format</param>
|
|
||||||
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
||||||
public BufferTextureBinding(
|
public BufferTextureBinding(
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
Format format,
|
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
Format = format;
|
|
||||||
IsImage = isImage;
|
IsImage = isImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
@ -64,6 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||||
|
Physical.TextureCache.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -86,11 +86,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
ImageBindings[i] = stage.Info.Images.Select(descriptor =>
|
ImageBindings[i] = stage.Info.Images.Select(descriptor =>
|
||||||
{
|
{
|
||||||
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
||||||
Format format = ShaderTexture.GetFormat(descriptor.Format);
|
FormatInfo formatInfo = ShaderTexture.GetFormatInfo(descriptor.Format);
|
||||||
|
|
||||||
var result = new TextureBindingInfo(
|
var result = new TextureBindingInfo(
|
||||||
target,
|
target,
|
||||||
format,
|
formatInfo,
|
||||||
descriptor.Set,
|
descriptor.Set,
|
||||||
descriptor.Binding,
|
descriptor.Binding,
|
||||||
descriptor.ArrayLength,
|
descriptor.ArrayLength,
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 7131;
|
private const uint CodeGenVersion = 7353;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|
|
@ -743,7 +743,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
constantBufferUsePerStageMask &= ~(1 << index);
|
constantBufferUsePerStageMask &= ~(1 << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkTextures)
|
if (checkTextures && _allTextures.Length > 0)
|
||||||
{
|
{
|
||||||
TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
|
TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
bool isLinear,
|
bool isLinear,
|
||||||
int gobBlocksInY,
|
int gobBlocksInY,
|
||||||
Format format,
|
Format format,
|
||||||
int bytesPerPixel,
|
byte bytesPerPixel,
|
||||||
ImageCrop crop,
|
ImageCrop crop,
|
||||||
Action<GpuContext, object> acquireCallback,
|
Action<GpuContext, object> acquireCallback,
|
||||||
Action<object> releaseCallback,
|
Action<object> releaseCallback,
|
||||||
|
|
106
src/Ryujinx.Graphics.OpenGL/Effects/AreaScalingFilter.cs
Normal file
106
src/Ryujinx.Graphics.OpenGL/Effects/AreaScalingFilter.cs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
using System;
|
||||||
|
using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal class AreaScalingFilter : IScalingFilter
|
||||||
|
{
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private int _inputUniform;
|
||||||
|
private int _outputUniform;
|
||||||
|
private int _srcX0Uniform;
|
||||||
|
private int _srcX1Uniform;
|
||||||
|
private int _srcY0Uniform;
|
||||||
|
private int _scalingShaderProgram;
|
||||||
|
private int _srcY1Uniform;
|
||||||
|
private int _dstX0Uniform;
|
||||||
|
private int _dstX1Uniform;
|
||||||
|
private int _dstY0Uniform;
|
||||||
|
private int _dstY1Uniform;
|
||||||
|
|
||||||
|
public float Level { get; set; }
|
||||||
|
|
||||||
|
public AreaScalingFilter(OpenGLRenderer renderer)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
_renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_scalingShaderProgram != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_scalingShaderProgram);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl");
|
||||||
|
|
||||||
|
_scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
_inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source");
|
||||||
|
_outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput");
|
||||||
|
|
||||||
|
_srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0");
|
||||||
|
_srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1");
|
||||||
|
_srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0");
|
||||||
|
_srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1");
|
||||||
|
_dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0");
|
||||||
|
_dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1");
|
||||||
|
_dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0");
|
||||||
|
_dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(
|
||||||
|
TextureView view,
|
||||||
|
TextureView destinationTexture,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extents2D source,
|
||||||
|
Extents2D destination)
|
||||||
|
{
|
||||||
|
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
|
||||||
|
GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
|
||||||
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
|
// Scaling pass
|
||||||
|
GL.UseProgram(_scalingShaderProgram);
|
||||||
|
view.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_srcX0Uniform, (float)source.X1);
|
||||||
|
GL.Uniform1(_srcX1Uniform, (float)source.X2);
|
||||||
|
GL.Uniform1(_srcY0Uniform, (float)source.Y1);
|
||||||
|
GL.Uniform1(_srcY1Uniform, (float)source.Y2);
|
||||||
|
GL.Uniform1(_dstX0Uniform, (float)destination.X1);
|
||||||
|
GL.Uniform1(_dstX1Uniform, (float)destination.X2);
|
||||||
|
GL.Uniform1(_dstY0Uniform, (float)destination.Y1);
|
||||||
|
GL.Uniform1(_dstY1Uniform, (float)destination.Y2);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
GL.UseProgram(previousProgram);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
||||||
|
|
||||||
|
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
private int _srcY0Uniform;
|
private int _srcY0Uniform;
|
||||||
private int _scalingShaderProgram;
|
private int _scalingShaderProgram;
|
||||||
private int _sharpeningShaderProgram;
|
private int _sharpeningShaderProgram;
|
||||||
private float _scale = 1;
|
private float _sharpeningLevel = 1;
|
||||||
private int _srcY1Uniform;
|
private int _srcY1Uniform;
|
||||||
private int _dstX0Uniform;
|
private int _dstX0Uniform;
|
||||||
private int _dstX1Uniform;
|
private int _dstX1Uniform;
|
||||||
|
@ -30,10 +30,10 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
|
||||||
public float Level
|
public float Level
|
||||||
{
|
{
|
||||||
get => _scale;
|
get => _sharpeningLevel;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_scale = MathF.Max(0.01f, value);
|
_sharpeningLevel = MathF.Max(0.01f, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
{
|
{
|
||||||
|
@ -6,18 +7,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
{
|
{
|
||||||
public static int CompileProgram(string shaderCode, ShaderType shaderType)
|
public static int CompileProgram(string shaderCode, ShaderType shaderType)
|
||||||
{
|
{
|
||||||
var shader = GL.CreateShader(shaderType);
|
return CompileProgram(new string[] { shaderCode }, shaderType);
|
||||||
GL.ShaderSource(shader, shaderCode);
|
|
||||||
GL.CompileShader(shader);
|
|
||||||
|
|
||||||
var program = GL.CreateProgram();
|
|
||||||
GL.AttachShader(program, shader);
|
|
||||||
GL.LinkProgram(program);
|
|
||||||
|
|
||||||
GL.DetachShader(program, shader);
|
|
||||||
GL.DeleteShader(shader);
|
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CompileProgram(string[] shaders, ShaderType shaderType)
|
public static int CompileProgram(string[] shaders, ShaderType shaderType)
|
||||||
|
@ -26,6 +16,15 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
|
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
|
||||||
GL.CompileShader(shader);
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
GL.GetShader(shader, ShaderParameter.CompileStatus, out int isCompiled);
|
||||||
|
if (isCompiled == 0)
|
||||||
|
{
|
||||||
|
string log = GL.GetShaderInfoLog(shader);
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, $"Failed to compile effect shader:\n\n{log}\n");
|
||||||
|
GL.DeleteShader(shader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
var program = GL.CreateProgram();
|
var program = GL.CreateProgram();
|
||||||
GL.AttachShader(program, shader);
|
GL.AttachShader(program, shader);
|
||||||
GL.LinkProgram(program);
|
GL.LinkProgram(program);
|
||||||
|
|
119
src/Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl
Normal file
119
src/Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#version 430 core
|
||||||
|
precision mediump float;
|
||||||
|
layout (local_size_x = 16, local_size_y = 16) in;
|
||||||
|
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
|
||||||
|
layout( location=1 ) uniform sampler2D Source;
|
||||||
|
layout( location=2 ) uniform float srcX0;
|
||||||
|
layout( location=3 ) uniform float srcX1;
|
||||||
|
layout( location=4 ) uniform float srcY0;
|
||||||
|
layout( location=5 ) uniform float srcY1;
|
||||||
|
layout( location=6 ) uniform float dstX0;
|
||||||
|
layout( location=7 ) uniform float dstX1;
|
||||||
|
layout( location=8 ) uniform float dstY0;
|
||||||
|
layout( location=9 ) uniform float dstY1;
|
||||||
|
|
||||||
|
/***** Area Sampling *****/
|
||||||
|
|
||||||
|
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
|
||||||
|
// Effectively a more accurate sharp bilinear filter when upscaling,
|
||||||
|
// that also works as a mathematically perfect downscale filter.
|
||||||
|
// https://entropymine.com/imageworsener/pixelmixing/
|
||||||
|
// https://github.com/obsproject/obs-studio/pull/1715
|
||||||
|
// https://legacy.imagemagick.org/Usage/filter/
|
||||||
|
vec4 AreaSampling(vec2 xy)
|
||||||
|
{
|
||||||
|
// Determine the sizes of the source and target images.
|
||||||
|
vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0));
|
||||||
|
vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0));
|
||||||
|
vec2 inverted_target_size = vec2(1.0) / target_size;
|
||||||
|
|
||||||
|
// Compute the top-left and bottom-right corners of the target pixel box.
|
||||||
|
vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
|
||||||
|
vec2 t_end = t_beg + vec2(1.0, 1.0);
|
||||||
|
|
||||||
|
// Convert the target pixel box to source pixel box.
|
||||||
|
vec2 beg = t_beg * inverted_target_size * source_size;
|
||||||
|
vec2 end = t_end * inverted_target_size * source_size;
|
||||||
|
|
||||||
|
// Compute the top-left and bottom-right corners of the pixel box.
|
||||||
|
ivec2 f_beg = ivec2(beg);
|
||||||
|
ivec2 f_end = ivec2(end);
|
||||||
|
|
||||||
|
// Compute how much of the start and end pixels are covered horizontally & vertically.
|
||||||
|
float area_w = 1.0 - fract(beg.x);
|
||||||
|
float area_n = 1.0 - fract(beg.y);
|
||||||
|
float area_e = fract(end.x);
|
||||||
|
float area_s = fract(end.y);
|
||||||
|
|
||||||
|
// Compute the areas of the corner pixels in the pixel box.
|
||||||
|
float area_nw = area_n * area_w;
|
||||||
|
float area_ne = area_n * area_e;
|
||||||
|
float area_sw = area_s * area_w;
|
||||||
|
float area_se = area_s * area_e;
|
||||||
|
|
||||||
|
// Initialize the color accumulator.
|
||||||
|
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
// Accumulate corner pixels.
|
||||||
|
avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
|
||||||
|
avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
|
||||||
|
avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
|
||||||
|
avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
|
||||||
|
|
||||||
|
// Determine the size of the pixel box.
|
||||||
|
int x_range = int(f_end.x - f_beg.x - 0.5);
|
||||||
|
int y_range = int(f_end.y - f_beg.y - 0.5);
|
||||||
|
|
||||||
|
// Accumulate top and bottom edge pixels.
|
||||||
|
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
||||||
|
{
|
||||||
|
avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
|
||||||
|
avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate left and right edge pixels and all the pixels in between.
|
||||||
|
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
|
||||||
|
{
|
||||||
|
avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
|
||||||
|
avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
|
||||||
|
|
||||||
|
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
||||||
|
{
|
||||||
|
avg_color += texelFetch(Source, ivec2(x, y), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the area of the pixel box that was sampled.
|
||||||
|
float area_corners = area_nw + area_ne + area_sw + area_se;
|
||||||
|
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
|
||||||
|
float area_center = float(x_range) * float(y_range);
|
||||||
|
|
||||||
|
// Return the normalized average color.
|
||||||
|
return avg_color / (area_corners + area_edges + area_center);
|
||||||
|
}
|
||||||
|
|
||||||
|
float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
|
||||||
|
vec2 s = step(bLeft, v) - step(tRight, v);
|
||||||
|
return s.x * s.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 translateDest(vec2 pos) {
|
||||||
|
vec2 translatedPos = vec2(pos.x, pos.y);
|
||||||
|
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
|
||||||
|
translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1 : translatedPos.y;
|
||||||
|
return translatedPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
|
||||||
|
vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
|
||||||
|
ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
|
||||||
|
if (insideBox(loc, bLeft, tRight) == 0) {
|
||||||
|
imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 outColor = AreaSampling(loc);
|
||||||
|
imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
|
@ -19,14 +18,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
_images = new TextureRef[size];
|
_images = new TextureRef[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFormats(int index, GAL.Format[] imageFormats)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < imageFormats.Length; i++)
|
|
||||||
{
|
|
||||||
_images[index + i].Format = imageFormats[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetImages(int index, ITexture[] images)
|
public void SetImages(int index, ITexture[] images)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < images.Length; i++)
|
for (int i = 0; i < images.Length; i++)
|
||||||
|
@ -36,6 +27,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
if (image is TextureBase imageBase)
|
if (image is TextureBase imageBase)
|
||||||
{
|
{
|
||||||
_images[index + i].Handle = imageBase.Handle;
|
_images[index + i].Handle = imageBase.Handle;
|
||||||
|
_images[index + i].Format = imageBase.Format;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
|
@ -55,9 +55,9 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(IMemoryOwner<byte> data)
|
public void SetData(MemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
var dataSpan = data.Memory.Span;
|
var dataSpan = data.Span;
|
||||||
|
|
||||||
Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
|
Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
|
||||||
|
|
||||||
|
@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
@ -448,13 +448,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(IMemoryOwner<byte> data)
|
public void SetData(MemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
using (data = EnsureDataFormat(data))
|
using (data = EnsureDataFormat(data))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
var dataSpan = data.Memory.Span;
|
var dataSpan = data.Span;
|
||||||
fixed (byte* ptr = dataSpan)
|
fixed (byte* ptr = dataSpan)
|
||||||
{
|
{
|
||||||
ReadFrom((IntPtr)ptr, dataSpan.Length);
|
ReadFrom((IntPtr)ptr, dataSpan.Length);
|
||||||
|
@ -463,13 +463,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
using (data = EnsureDataFormat(data))
|
using (data = EnsureDataFormat(data))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data.Memory.Span)
|
fixed (byte* ptr = data.Span)
|
||||||
{
|
{
|
||||||
int width = Math.Max(Info.Width >> level, 1);
|
int width = Math.Max(Info.Width >> level, 1);
|
||||||
int height = Math.Max(Info.Height >> level, 1);
|
int height = Math.Max(Info.Height >> level, 1);
|
||||||
|
@ -480,7 +480,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
using (data = EnsureDataFormat(data))
|
using (data = EnsureDataFormat(data))
|
||||||
{
|
{
|
||||||
|
@ -489,7 +489,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data.Memory.Span)
|
fixed (byte* ptr = data.Span)
|
||||||
{
|
{
|
||||||
ReadFrom2D(
|
ReadFrom2D(
|
||||||
(IntPtr)ptr,
|
(IntPtr)ptr,
|
||||||
|
@ -522,13 +522,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
|
ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMemoryOwner<byte> EnsureDataFormat(IMemoryOwner<byte> data)
|
private MemoryOwner<byte> EnsureDataFormat(MemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
if (Format == Format.S8UintD24Unorm)
|
if (Format == Format.S8UintD24Unorm)
|
||||||
{
|
{
|
||||||
using (data)
|
using (data)
|
||||||
{
|
{
|
||||||
return FormatConverter.ConvertS8D24ToD24S8(data.Memory.Span);
|
return FormatConverter.ConvertS8D24ToD24S8(data.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,7 +202,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
shaderSubgroupSize: Constants.MaxSubgroupSize,
|
shaderSubgroupSize: Constants.MaxSubgroupSize,
|
||||||
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
||||||
textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment,
|
textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment,
|
||||||
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
|
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan.
|
||||||
|
maximumGpuMemory: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
|
private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
|
||||||
|
|
||||||
private readonly (TextureBase, Format)[] _images;
|
private readonly TextureBase[] _images;
|
||||||
private TextureBase _unit0Texture;
|
private TextureBase _unit0Texture;
|
||||||
private Sampler _unit0Sampler;
|
private Sampler _unit0Sampler;
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
_fragmentOutputMap = uint.MaxValue;
|
_fragmentOutputMap = uint.MaxValue;
|
||||||
_componentMasks = uint.MaxValue;
|
_componentMasks = uint.MaxValue;
|
||||||
|
|
||||||
_images = new (TextureBase, Format)[SavedImages];
|
_images = new TextureBase[SavedImages];
|
||||||
|
|
||||||
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
|
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
|
||||||
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
|
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
|
||||||
|
@ -935,11 +935,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
SetFrontFace(_frontFace = frontFace.Convert());
|
SetFrontFace(_frontFace = frontFace.Convert());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
public void SetImage(ShaderStage stage, int binding, ITexture texture)
|
||||||
{
|
{
|
||||||
if ((uint)binding < SavedImages)
|
if ((uint)binding < SavedImages)
|
||||||
{
|
{
|
||||||
_images[binding] = (texture as TextureBase, imageFormat);
|
_images[binding] = texture as TextureBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
|
@ -950,7 +950,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
TextureBase texBase = (TextureBase)texture;
|
TextureBase texBase = (TextureBase)texture;
|
||||||
|
|
||||||
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
|
SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
|
||||||
|
|
||||||
if (format != 0)
|
if (format != 0)
|
||||||
{
|
{
|
||||||
|
@ -1622,11 +1622,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
{
|
{
|
||||||
for (int i = 0; i < SavedImages; i++)
|
for (int i = 0; i < SavedImages; i++)
|
||||||
{
|
{
|
||||||
(TextureBase texBase, Format imageFormat) = _images[i];
|
TextureBase texBase = _images[i];
|
||||||
|
|
||||||
if (texBase != null)
|
if (texBase != null)
|
||||||
{
|
{
|
||||||
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
|
SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
|
||||||
|
|
||||||
if (format != 0)
|
if (format != 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
|
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
|
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
|
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\area_scaling.glsl" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -373,6 +373,16 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
_isLinear = false;
|
_isLinear = false;
|
||||||
_scalingFilter.Level = _scalingFilterLevel;
|
_scalingFilter.Level = _scalingFilterLevel;
|
||||||
|
|
||||||
|
RecreateUpscalingTexture();
|
||||||
|
break;
|
||||||
|
case ScalingFilter.Area:
|
||||||
|
if (_scalingFilter is not AreaScalingFilter)
|
||||||
|
{
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
|
_scalingFilter = new AreaScalingFilter(_renderer);
|
||||||
|
}
|
||||||
|
_isLinear = false;
|
||||||
|
|
||||||
RecreateUpscalingTexture();
|
RecreateUpscalingTexture();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,30 +222,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AtomOp.And:
|
case AtomOp.Min:
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
if (type == AtomSize.S32)
|
||||||
{
|
{
|
||||||
res = context.AtomicAnd(storageKind, e0, e1, value);
|
res = context.AtomicMinS32(storageKind, e0, e1, value);
|
||||||
}
|
}
|
||||||
else
|
else if (type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
res = context.AtomicMinU32(storageKind, e0, e1, value);
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AtomOp.Xor:
|
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
|
||||||
{
|
|
||||||
res = context.AtomicXor(storageKind, e0, e1, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AtomOp.Or:
|
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
|
||||||
{
|
|
||||||
res = context.AtomicOr(storageKind, e0, e1, value);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -266,20 +250,49 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AtomOp.Min:
|
case AtomOp.And:
|
||||||
if (type == AtomSize.S32)
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicMinS32(storageKind, e0, e1, value);
|
res = context.AtomicAnd(storageKind, e0, e1, value);
|
||||||
}
|
|
||||||
else if (type == AtomSize.U32)
|
|
||||||
{
|
|
||||||
res = context.AtomicMinU32(storageKind, e0, e1, value);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case AtomOp.Or:
|
||||||
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
|
{
|
||||||
|
res = context.AtomicOr(storageKind, e0, e1, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AtomOp.Xor:
|
||||||
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
|
{
|
||||||
|
res = context.AtomicXor(storageKind, e0, e1, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AtomOp.Exch:
|
||||||
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
|
{
|
||||||
|
res = context.AtomicSwap(storageKind, e0, e1, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid atomic operation: {op}.");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -138,6 +138,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
// Ensure that conditions met for that branch are also met for the current one.
|
// Ensure that conditions met for that branch are also met for the current one.
|
||||||
// Prefer the latest sources for the phi node.
|
// Prefer the latest sources for the phi node.
|
||||||
|
|
||||||
|
int undefCount = 0;
|
||||||
|
|
||||||
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
BasicBlock phiBlock = phiNode.GetBlock(i);
|
BasicBlock phiBlock = phiNode.GetBlock(i);
|
||||||
|
@ -159,6 +161,26 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (phiSource.Type == OperandType.Undefined)
|
||||||
|
{
|
||||||
|
undefCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all sources but one are undefined, we can assume that the one
|
||||||
|
// that is not undefined is the right one.
|
||||||
|
|
||||||
|
if (undefCount == phiNode.SourcesCount - 1)
|
||||||
|
{
|
||||||
|
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
Operand phiSource = phiNode.GetSource(i);
|
||||||
|
|
||||||
|
if (phiSource.Type != OperandType.Undefined)
|
||||||
|
{
|
||||||
|
return phiSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
if (stage == ShaderStage.Vertex)
|
if (stage == ShaderStage.Vertex)
|
||||||
{
|
{
|
||||||
InitializePositionOutput(context);
|
InitializeVertexOutputs(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents;
|
UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents;
|
||||||
|
@ -236,12 +236,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializePositionOutput(EmitterContext context)
|
private static void InitializeVertexOutputs(EmitterContext context)
|
||||||
{
|
{
|
||||||
for (int c = 0; c < 4; c++)
|
for (int c = 0; c < 4; c++)
|
||||||
{
|
{
|
||||||
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
|
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.Program.ClipDistancesWritten != 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
context.Store(StorageKind.Output, IoVariable.ClipDistance, null, Const(i), ConstF(0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
|
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
|
||||||
|
|
|
@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
CommandBuffer
|
CommandBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _feedbackLoopActive;
|
||||||
private PipelineStageFlags _incoherentBufferWriteStages;
|
private PipelineStageFlags _incoherentBufferWriteStages;
|
||||||
private PipelineStageFlags _incoherentTextureWriteStages;
|
private PipelineStageFlags _incoherentTextureWriteStages;
|
||||||
private PipelineStageFlags _extraStages;
|
private PipelineStageFlags _extraStages;
|
||||||
private IncoherentBarrierType _queuedIncoherentBarrier;
|
private IncoherentBarrierType _queuedIncoherentBarrier;
|
||||||
|
private bool _queuedFeedbackLoopBarrier;
|
||||||
|
|
||||||
public BarrierBatch(VulkanRenderer gd)
|
public BarrierBatch(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
|
@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
stages |= PipelineStageFlags.TransformFeedbackBitExt;
|
stages |= PipelineStageFlags.TransformFeedbackBitExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gd.IsTBDR)
|
|
||||||
{
|
|
||||||
// Desktop GPUs can transform image barriers into memory barriers.
|
|
||||||
|
|
||||||
access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
|
|
||||||
access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
|
|
||||||
|
|
||||||
stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
|
|
||||||
stages |= PipelineStageFlags.ColorAttachmentOutputBit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (access, stages);
|
return (access, stages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
|
|
||||||
_queuedIncoherentBarrier = IncoherentBarrierType.None;
|
_queuedIncoherentBarrier = IncoherentBarrierType.None;
|
||||||
|
_queuedFeedbackLoopBarrier = false;
|
||||||
}
|
}
|
||||||
|
else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
|
||||||
|
{
|
||||||
|
// Feedback loop barrier.
|
||||||
|
|
||||||
|
MemoryBarrier barrier = new MemoryBarrier()
|
||||||
|
{
|
||||||
|
SType = StructureType.MemoryBarrier,
|
||||||
|
SrcAccessMask = AccessFlags.ShaderWriteBit,
|
||||||
|
DstAccessMask = AccessFlags.ShaderReadBit
|
||||||
|
};
|
||||||
|
|
||||||
|
QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
|
||||||
|
|
||||||
|
_queuedFeedbackLoopBarrier = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_feedbackLoopActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||||
{
|
{
|
||||||
Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
|
Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||||
{
|
{
|
||||||
if (program != null)
|
if (program != null)
|
||||||
{
|
{
|
||||||
|
@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
|
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_feedbackLoopActive |= feedbackLoopActive;
|
||||||
|
|
||||||
FlushMemoryBarrier(program, inRenderPass);
|
FlushMemoryBarrier(program, inRenderPass);
|
||||||
|
|
||||||
if (!inRenderPass && rpHolder != null)
|
if (!inRenderPass && rpHolder != null)
|
||||||
|
@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_queuedIncoherentBarrier = type;
|
_queuedIncoherentBarrier = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_queuedFeedbackLoopBarrier = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueTextureBarrier()
|
public void QueueTextureBarrier()
|
||||||
|
|
|
@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Range = (uint)size,
|
Range = (uint)size,
|
||||||
};
|
};
|
||||||
|
|
||||||
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
_gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
||||||
|
|
||||||
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer);
|
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PipelineStageFlags.AllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
DependencyFlags.DeviceGroupBit,
|
DependencyFlags.DeviceGroupBit,
|
||||||
1,
|
1,
|
||||||
memoryBarrier,
|
in memoryBarrier,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
|
@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
1,
|
1,
|
||||||
memoryBarrier,
|
in memoryBarrier,
|
||||||
0,
|
0,
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PBufferBinds = &bufferBind
|
PBufferBinds = &bufferBind
|
||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
|
gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
|
var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
|
||||||
|
|
|
@ -25,7 +25,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
var buffer = _buffer.Get(cbs, _offset, _size, true).Value;
|
var buffer = _buffer.Get(cbs, _offset, _size, true).Value;
|
||||||
|
|
||||||
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size);
|
ulong offset = (ulong)_offset;
|
||||||
|
ulong size = (ulong)_size;
|
||||||
|
|
||||||
|
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Level = CommandBufferLevel.Primary,
|
Level = CommandBufferLevel.Primary,
|
||||||
};
|
};
|
||||||
|
|
||||||
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
|
api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer);
|
||||||
|
|
||||||
Dependants = new List<IAuto>();
|
Dependants = new List<IAuto>();
|
||||||
Waitables = new List<MultiFenceHolder>();
|
Waitables = new List<MultiFenceHolder>();
|
||||||
|
@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
CommandPoolCreateFlags.ResetCommandBufferBit,
|
CommandPoolCreateFlags.ResetCommandBufferBit,
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||||
|
|
||||||
// We need at least 2 command buffers to get texture data in some cases.
|
// We need at least 2 command buffers to get texture data in some cases.
|
||||||
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
|
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
|
||||||
|
@ -253,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
SType = StructureType.CommandBufferBeginInfo,
|
SType = StructureType.CommandBufferBeginInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError();
|
_api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError();
|
||||||
|
|
||||||
return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
|
return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
lock (_queueLock)
|
lock (_queueLock)
|
||||||
{
|
{
|
||||||
_api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
|
_api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PBufferInfo = &bufferInfo,
|
PBufferInfo = &bufferInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PBufferInfo = pBufferInfo,
|
PBufferInfo = pBufferInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PImageInfo = &imageInfo,
|
PImageInfo = &imageInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PImageInfo = pImageInfo,
|
PImageInfo = pImageInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PImageInfo = pImageInfo,
|
PImageInfo = pImageInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
|
|
||||||
i += count - 1;
|
i += count - 1;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PTexelBufferView = &texelBufferView,
|
PTexelBufferView = &texelBufferView,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PTexelBufferView = pTexelBufferView + i,
|
PTexelBufferView = pTexelBufferView + i,
|
||||||
};
|
};
|
||||||
|
|
||||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
i += count;
|
i += count;
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PPoolSizes = pPoolsSize,
|
PPoolSizes = pPoolsSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
|
Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
|
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
|
||||||
|
@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private record struct TextureRef
|
private record struct TextureRef
|
||||||
{
|
{
|
||||||
public ShaderStage Stage;
|
public ShaderStage Stage;
|
||||||
public TextureStorage Storage;
|
public TextureView View;
|
||||||
public Auto<DisposableImageView> View;
|
public Auto<DisposableImageView> ImageView;
|
||||||
public Auto<DisposableSampler> Sampler;
|
public Auto<DisposableSampler> Sampler;
|
||||||
|
|
||||||
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
|
public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Storage = storage;
|
|
||||||
View = view;
|
View = view;
|
||||||
|
ImageView = imageView;
|
||||||
Sampler = sampler;
|
Sampler = sampler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private record struct ImageRef
|
private record struct ImageRef
|
||||||
{
|
{
|
||||||
public ShaderStage Stage;
|
public ShaderStage Stage;
|
||||||
public TextureStorage Storage;
|
public TextureView View;
|
||||||
public Auto<DisposableImageView> View;
|
public Auto<DisposableImageView> ImageView;
|
||||||
|
|
||||||
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
|
public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Storage = storage;
|
|
||||||
View = view;
|
View = view;
|
||||||
|
ImageView = imageView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +82,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly ImageRef[] _imageRefs;
|
private readonly ImageRef[] _imageRefs;
|
||||||
private readonly TextureBuffer[] _bufferTextureRefs;
|
private readonly TextureBuffer[] _bufferTextureRefs;
|
||||||
private readonly TextureBuffer[] _bufferImageRefs;
|
private readonly TextureBuffer[] _bufferImageRefs;
|
||||||
private readonly Format[] _bufferImageFormats;
|
|
||||||
|
|
||||||
private ArrayRef<TextureArray>[] _textureArrayRefs;
|
private ArrayRef<TextureArray>[] _textureArrayRefs;
|
||||||
private ArrayRef<ImageArray>[] _imageArrayRefs;
|
private ArrayRef<ImageArray>[] _imageArrayRefs;
|
||||||
|
@ -124,6 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly TextureView _dummyTexture;
|
private readonly TextureView _dummyTexture;
|
||||||
private readonly SamplerHolder _dummySampler;
|
private readonly SamplerHolder _dummySampler;
|
||||||
|
|
||||||
|
public List<TextureView> FeedbackLoopHazards { get; private set; }
|
||||||
|
|
||||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
|
@ -138,7 +140,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
|
_imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
|
||||||
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
|
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
|
||||||
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
|
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
|
||||||
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
|
|
||||||
|
|
||||||
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
|
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
|
||||||
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
|
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
|
||||||
|
@ -209,10 +210,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_templateUpdater = new();
|
_templateUpdater = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize(bool isMainPipeline)
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
||||||
_dummyTexture.SetData(dummyTextureData);
|
_dummyTexture.SetData(dummyTextureData);
|
||||||
|
|
||||||
|
if (isMainPipeline)
|
||||||
|
{
|
||||||
|
FeedbackLoopHazards = new();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
|
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
|
||||||
|
@ -275,6 +281,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void InsertBindingBarriers(CommandBufferScoped cbs)
|
public void InsertBindingBarriers(CommandBufferScoped cbs)
|
||||||
{
|
{
|
||||||
|
if ((FeedbackLoopHazards?.Count ?? 0) > 0)
|
||||||
|
{
|
||||||
|
// Clear existing hazards - they will be rebuilt.
|
||||||
|
|
||||||
|
foreach (TextureView hazard in FeedbackLoopHazards)
|
||||||
|
{
|
||||||
|
hazard.DecrementHazardUses();
|
||||||
|
}
|
||||||
|
|
||||||
|
FeedbackLoopHazards.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
|
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
|
||||||
{
|
{
|
||||||
if (segment.Type == ResourceType.TextureAndSampler)
|
if (segment.Type == ResourceType.TextureAndSampler)
|
||||||
|
@ -284,7 +302,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
for (int i = 0; i < segment.Count; i++)
|
for (int i = 0; i < segment.Count; i++)
|
||||||
{
|
{
|
||||||
ref var texture = ref _textureRefs[segment.Binding + i];
|
ref var texture = ref _textureRefs[segment.Binding + i];
|
||||||
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
|
texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -305,7 +323,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
for (int i = 0; i < segment.Count; i++)
|
for (int i = 0; i < segment.Count; i++)
|
||||||
{
|
{
|
||||||
ref var image = ref _imageRefs[segment.Binding + i];
|
ref var image = ref _imageRefs[segment.Binding + i];
|
||||||
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
|
image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -371,29 +389,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_dirty = DirtyFlags.All;
|
_dirty = DirtyFlags.All;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(
|
public void SetImage(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture image)
|
||||||
CommandBufferScoped cbs,
|
|
||||||
ShaderStage stage,
|
|
||||||
int binding,
|
|
||||||
ITexture image,
|
|
||||||
Format imageFormat)
|
|
||||||
{
|
{
|
||||||
if (image is TextureBuffer imageBuffer)
|
if (image is TextureBuffer imageBuffer)
|
||||||
{
|
{
|
||||||
_bufferImageRefs[binding] = imageBuffer;
|
_bufferImageRefs[binding] = imageBuffer;
|
||||||
_bufferImageFormats[binding] = imageFormat;
|
|
||||||
}
|
}
|
||||||
else if (image is TextureView view)
|
else if (image is TextureView view)
|
||||||
{
|
{
|
||||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
ref ImageRef iRef = ref _imageRefs[binding];
|
||||||
|
|
||||||
_imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
|
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
||||||
|
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
|
|
||||||
|
iRef = new(stage, view, view.GetIdentityImageView());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_imageRefs[binding] = default;
|
_imageRefs[binding] = default;
|
||||||
_bufferImageRefs[binding] = null;
|
_bufferImageRefs[binding] = null;
|
||||||
_bufferImageFormats[binding] = default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalDirty(DirtyFlags.Image);
|
SignalDirty(DirtyFlags.Image);
|
||||||
|
@ -486,9 +500,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
else if (texture is TextureView view)
|
else if (texture is TextureView view)
|
||||||
{
|
{
|
||||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
ref TextureRef iRef = ref _textureRefs[binding];
|
||||||
|
|
||||||
_textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
||||||
|
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
|
|
||||||
|
iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -510,7 +527,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||||
|
|
||||||
_textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
_textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||||
|
|
||||||
SignalDirty(DirtyFlags.Texture);
|
SignalDirty(DirtyFlags.Texture);
|
||||||
}
|
}
|
||||||
|
@ -836,7 +853,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
ref var texture = ref textures[i];
|
ref var texture = ref textures[i];
|
||||||
ref var refs = ref _textureRefs[binding + i];
|
ref var refs = ref _textureRefs[binding + i];
|
||||||
|
|
||||||
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
|
||||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
if (texture.ImageView.Handle == 0)
|
||||||
|
@ -886,7 +903,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
|
images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
tu.Push<DescriptorImageInfo>(images[..count]);
|
tu.Push<DescriptorImageInfo>(images[..count]);
|
||||||
|
@ -897,7 +914,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default;
|
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
tu.Push<BufferView>(bufferImages[..count]);
|
tu.Push<BufferView>(bufferImages[..count]);
|
||||||
|
@ -957,7 +974,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
ref var texture = ref textures[i];
|
ref var texture = ref textures[i];
|
||||||
ref var refs = ref _textureRefs[binding + i];
|
ref var refs = ref _textureRefs[binding + i];
|
||||||
|
|
||||||
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
|
||||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
if (texture.ImageView.Handle == 0)
|
||||||
|
|
101
src/Ryujinx.Graphics.Vulkan/Effects/AreaScalingFilter.cs
Normal file
101
src/Ryujinx.Graphics.Vulkan/Effects/AreaScalingFilter.cs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using Extent2D = Ryujinx.Graphics.GAL.Extents2D;
|
||||||
|
using Format = Silk.NET.Vulkan.Format;
|
||||||
|
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
internal class AreaScalingFilter : IScalingFilter
|
||||||
|
{
|
||||||
|
private readonly VulkanRenderer _renderer;
|
||||||
|
private PipelineHelperShader _pipeline;
|
||||||
|
private ISampler _sampler;
|
||||||
|
private ShaderCollection _scalingProgram;
|
||||||
|
private Device _device;
|
||||||
|
|
||||||
|
public float Level { get; set; }
|
||||||
|
|
||||||
|
public AreaScalingFilter(VulkanRenderer renderer, Device device)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_renderer = renderer;
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_pipeline.Dispose();
|
||||||
|
_scalingProgram.Dispose();
|
||||||
|
_sampler.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_pipeline = new PipelineHelperShader(_renderer, _device);
|
||||||
|
|
||||||
|
_pipeline.Initialize();
|
||||||
|
|
||||||
|
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv");
|
||||||
|
|
||||||
|
var scalingResourceLayout = new ResourceLayoutBuilder()
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
|
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
|
}, scalingResourceLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(
|
||||||
|
TextureView view,
|
||||||
|
CommandBufferScoped cbs,
|
||||||
|
Auto<DisposableImageView> destinationTexture,
|
||||||
|
Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extent2D source,
|
||||||
|
Extent2D destination)
|
||||||
|
{
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_scalingProgram);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler);
|
||||||
|
|
||||||
|
ReadOnlySpan<float> dimensionsBuffer = stackalloc float[]
|
||||||
|
{
|
||||||
|
source.X1,
|
||||||
|
source.X2,
|
||||||
|
source.Y1,
|
||||||
|
source.Y2,
|
||||||
|
destination.X1,
|
||||||
|
destination.X2,
|
||||||
|
destination.Y1,
|
||||||
|
destination.Y2,
|
||||||
|
};
|
||||||
|
|
||||||
|
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
||||||
|
using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize);
|
||||||
|
buffer.Holder.SetDataUnchecked(buffer.Offset, dimensionsBuffer);
|
||||||
|
|
||||||
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||||
|
_pipeline.SetImage(0, destinationTexture);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
_pipeline.Finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _texture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
122
src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.glsl
Normal file
122
src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.glsl
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// Scaling
|
||||||
|
|
||||||
|
#version 430 core
|
||||||
|
layout (local_size_x = 16, local_size_y = 16) in;
|
||||||
|
layout( rgba8, binding = 0, set = 3) uniform image2D imgOutput;
|
||||||
|
layout( binding = 1, set = 2) uniform sampler2D Source;
|
||||||
|
layout( binding = 2 ) uniform dimensions{
|
||||||
|
float srcX0;
|
||||||
|
float srcX1;
|
||||||
|
float srcY0;
|
||||||
|
float srcY1;
|
||||||
|
float dstX0;
|
||||||
|
float dstX1;
|
||||||
|
float dstY0;
|
||||||
|
float dstY1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***** Area Sampling *****/
|
||||||
|
|
||||||
|
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
|
||||||
|
// Effectively a more accurate sharp bilinear filter when upscaling,
|
||||||
|
// that also works as a mathematically perfect downscale filter.
|
||||||
|
// https://entropymine.com/imageworsener/pixelmixing/
|
||||||
|
// https://github.com/obsproject/obs-studio/pull/1715
|
||||||
|
// https://legacy.imagemagick.org/Usage/filter/
|
||||||
|
vec4 AreaSampling(vec2 xy)
|
||||||
|
{
|
||||||
|
// Determine the sizes of the source and target images.
|
||||||
|
vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0));
|
||||||
|
vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0));
|
||||||
|
vec2 inverted_target_size = vec2(1.0) / target_size;
|
||||||
|
|
||||||
|
// Compute the top-left and bottom-right corners of the target pixel box.
|
||||||
|
vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
|
||||||
|
vec2 t_end = t_beg + vec2(1.0, 1.0);
|
||||||
|
|
||||||
|
// Convert the target pixel box to source pixel box.
|
||||||
|
vec2 beg = t_beg * inverted_target_size * source_size;
|
||||||
|
vec2 end = t_end * inverted_target_size * source_size;
|
||||||
|
|
||||||
|
// Compute the top-left and bottom-right corners of the pixel box.
|
||||||
|
ivec2 f_beg = ivec2(beg);
|
||||||
|
ivec2 f_end = ivec2(end);
|
||||||
|
|
||||||
|
// Compute how much of the start and end pixels are covered horizontally & vertically.
|
||||||
|
float area_w = 1.0 - fract(beg.x);
|
||||||
|
float area_n = 1.0 - fract(beg.y);
|
||||||
|
float area_e = fract(end.x);
|
||||||
|
float area_s = fract(end.y);
|
||||||
|
|
||||||
|
// Compute the areas of the corner pixels in the pixel box.
|
||||||
|
float area_nw = area_n * area_w;
|
||||||
|
float area_ne = area_n * area_e;
|
||||||
|
float area_sw = area_s * area_w;
|
||||||
|
float area_se = area_s * area_e;
|
||||||
|
|
||||||
|
// Initialize the color accumulator.
|
||||||
|
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
// Accumulate corner pixels.
|
||||||
|
avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
|
||||||
|
avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
|
||||||
|
avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
|
||||||
|
avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
|
||||||
|
|
||||||
|
// Determine the size of the pixel box.
|
||||||
|
int x_range = int(f_end.x - f_beg.x - 0.5);
|
||||||
|
int y_range = int(f_end.y - f_beg.y - 0.5);
|
||||||
|
|
||||||
|
// Accumulate top and bottom edge pixels.
|
||||||
|
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
||||||
|
{
|
||||||
|
avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
|
||||||
|
avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate left and right edge pixels and all the pixels in between.
|
||||||
|
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
|
||||||
|
{
|
||||||
|
avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
|
||||||
|
avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
|
||||||
|
|
||||||
|
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
||||||
|
{
|
||||||
|
avg_color += texelFetch(Source, ivec2(x, y), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the area of the pixel box that was sampled.
|
||||||
|
float area_corners = area_nw + area_ne + area_sw + area_se;
|
||||||
|
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
|
||||||
|
float area_center = float(x_range) * float(y_range);
|
||||||
|
|
||||||
|
// Return the normalized average color.
|
||||||
|
return avg_color / (area_corners + area_edges + area_center);
|
||||||
|
}
|
||||||
|
|
||||||
|
float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
|
||||||
|
vec2 s = step(bLeft, v) - step(tRight, v);
|
||||||
|
return s.x * s.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 translateDest(vec2 pos) {
|
||||||
|
vec2 translatedPos = vec2(pos.x, pos.y);
|
||||||
|
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
|
||||||
|
translatedPos.y = dstY0 < dstY1 ? dstY1 + dstY0 - translatedPos.y - 1 : translatedPos.y;
|
||||||
|
return translatedPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
|
||||||
|
vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
|
||||||
|
ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
|
||||||
|
if (insideBox(loc, bLeft, tRight) == 0) {
|
||||||
|
imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 outColor = AreaSampling(loc);
|
||||||
|
imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
|
||||||
|
}
|
BIN
src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv
Normal file
BIN
src/Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv
Normal file
Binary file not shown.
|
@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
|
||||||
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
|
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
_pipeline.Specialize(_specConstants);
|
_pipeline.Specialize(_specConstants);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
|
12
src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
Normal file
12
src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
internal enum FeedbackLoopAspects
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Color = 1 << 0,
|
||||||
|
Depth = 1 << 1,
|
||||||
|
}
|
||||||
|
}
|
|
@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Layers = Layers,
|
Layers = Layers,
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_depthStencil?.Storage?.AddStoreOpUsage(true);
|
_depthStencil?.Storage?.AddStoreOpUsage(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ClearBindings()
|
||||||
|
{
|
||||||
|
_depthStencil?.Storage.ClearBindings();
|
||||||
|
|
||||||
|
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||||
|
{
|
||||||
|
_colorsCanonical[i]?.Storage.ClearBindings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddBindings()
|
||||||
|
{
|
||||||
|
_depthStencil?.Storage.AddBinding(_depthStencil);
|
||||||
|
|
||||||
|
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||||
|
{
|
||||||
|
TextureView color = _colorsCanonical[i];
|
||||||
|
color?.Storage.AddBinding(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
|
|
|
@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
public readonly bool SupportsViewportArray2;
|
public readonly bool SupportsViewportArray2;
|
||||||
public readonly bool SupportsHostImportedMemory;
|
public readonly bool SupportsHostImportedMemory;
|
||||||
public readonly bool SupportsDepthClipControl;
|
public readonly bool SupportsDepthClipControl;
|
||||||
|
public readonly bool SupportsAttachmentFeedbackLoop;
|
||||||
|
public readonly bool SupportsDynamicAttachmentFeedbackLoop;
|
||||||
public readonly uint SubgroupSize;
|
public readonly uint SubgroupSize;
|
||||||
public readonly SampleCountFlags SupportedSampleCounts;
|
public readonly SampleCountFlags SupportedSampleCounts;
|
||||||
public readonly PortabilitySubsetFlags PortabilitySubset;
|
public readonly PortabilitySubsetFlags PortabilitySubset;
|
||||||
|
@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
bool supportsViewportArray2,
|
bool supportsViewportArray2,
|
||||||
bool supportsHostImportedMemory,
|
bool supportsHostImportedMemory,
|
||||||
bool supportsDepthClipControl,
|
bool supportsDepthClipControl,
|
||||||
|
bool supportsAttachmentFeedbackLoop,
|
||||||
|
bool supportsDynamicAttachmentFeedbackLoop,
|
||||||
uint subgroupSize,
|
uint subgroupSize,
|
||||||
SampleCountFlags supportedSampleCounts,
|
SampleCountFlags supportedSampleCounts,
|
||||||
PortabilitySubsetFlags portabilitySubset,
|
PortabilitySubsetFlags portabilitySubset,
|
||||||
|
@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
SupportsViewportArray2 = supportsViewportArray2;
|
SupportsViewportArray2 = supportsViewportArray2;
|
||||||
SupportsHostImportedMemory = supportsHostImportedMemory;
|
SupportsHostImportedMemory = supportsHostImportedMemory;
|
||||||
SupportsDepthClipControl = supportsDepthClipControl;
|
SupportsDepthClipControl = supportsDepthClipControl;
|
||||||
|
SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
|
||||||
|
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
|
||||||
SubgroupSize = subgroupSize;
|
SubgroupSize = subgroupSize;
|
||||||
SupportedSampleCounts = supportedSampleCounts;
|
SupportedSampleCounts = supportedSampleCounts;
|
||||||
PortabilitySubset = portabilitySubset;
|
PortabilitySubset = portabilitySubset;
|
||||||
|
|
|
@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
|
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, dstFormat);
|
_pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(dstFormat));
|
||||||
|
|
||||||
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
|
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
|
||||||
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
|
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
|
||||||
|
@ -1168,7 +1168,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
|
_pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format));
|
||||||
|
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PNext = &importInfo,
|
PNext = &importInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory);
|
Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory);
|
||||||
|
|
||||||
if (result < Result.Success)
|
if (result < Result.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
public TextureStorage Storage;
|
public TextureStorage Storage;
|
||||||
public TextureView View;
|
public TextureView View;
|
||||||
public GAL.Format ImageFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly TextureRef[] _textureRefs;
|
private readonly TextureRef[] _textureRefs;
|
||||||
|
@ -52,16 +51,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_isBuffer = isBuffer;
|
_isBuffer = isBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFormats(int index, GAL.Format[] imageFormats)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < imageFormats.Length; i++)
|
|
||||||
{
|
|
||||||
_textureRefs[index + i].ImageFormat = imageFormats[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
SetDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetImages(int index, ITexture[] images)
|
public void SetImages(int index, ITexture[] images)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < images.Length; i++)
|
for (int i = 0; i < images.Length; i++)
|
||||||
|
@ -142,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
ref var texture = ref textures[i];
|
ref var texture = ref textures[i];
|
||||||
ref var refs = ref _textureRefs[i];
|
ref var refs = ref _textureRefs[i];
|
||||||
|
|
||||||
if (i > 0 && _textureRefs[i - 1].View == refs.View && _textureRefs[i - 1].ImageFormat == refs.ImageFormat)
|
if (i > 0 && _textureRefs[i - 1].View == refs.View)
|
||||||
{
|
{
|
||||||
texture = textures[i - 1];
|
texture = textures[i - 1];
|
||||||
|
|
||||||
|
@ -150,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
|
|
||||||
texture.ImageLayout = ImageLayout.General;
|
texture.ImageLayout = ImageLayout.General;
|
||||||
texture.ImageView = refs.View?.GetView(refs.ImageFormat).GetIdentityImageView().Get(cbs).Value ?? default;
|
texture.ImageView = refs.View?.GetIdentityImageView().Get(cbs).Value ?? default;
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
if (texture.ImageView.Handle == 0)
|
||||||
{
|
{
|
||||||
|
@ -167,7 +156,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
for (int i = 0; i < bufferTextures.Length; i++)
|
for (int i = 0; i < bufferTextures.Length; i++)
|
||||||
{
|
{
|
||||||
bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, _textureRefs[i].ImageFormat, true) ?? default;
|
bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, true) ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bufferTextures;
|
return bufferTextures;
|
||||||
|
|
|
@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
MemoryTypeIndex = (uint)MemoryTypeIndex,
|
MemoryTypeIndex = (uint)MemoryTypeIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
_api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
|
_api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
|
||||||
|
|
||||||
IntPtr hostPointer = IntPtr.Zero;
|
IntPtr hostPointer = IntPtr.Zero;
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue