Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cd124bda58 | ||
|
9f12e50a54 | ||
|
097562bc6c | ||
|
db4242c5dc | ||
|
4dd77316f7 | ||
|
3f98369a17 | ||
|
c26aeefe03 | ||
|
666e05f5cb | ||
|
8d9d508dc7 | ||
|
e27f5522e2 | ||
|
add2a9d151 | ||
|
9e50dd99d7 | ||
|
0dec91bb42 | ||
|
d9b63353b0 | ||
|
eabd0ec93f | ||
|
138d5dc64a | ||
|
3e68a87d63 | ||
|
69b6ef7a4a | ||
|
40e87c634e | ||
|
79d1c190db | ||
|
2bc88467eb | ||
|
baf8752e74 | ||
|
d5e4378aea | ||
|
6dbcdfea47 | ||
|
c5258cf082 | ||
|
5c89e22bb9 | ||
|
11ecff2ff0 | ||
|
4c3f09644a | ||
|
e187a8870a | ||
|
a64fee29dc | ||
|
9ef94c8292 | ||
|
915d6d044c | ||
|
a4780ab33b | ||
|
a947a45d81 | ||
|
9db73f74cf | ||
|
a1efd87c45 | ||
|
49be977588 |
@@ -226,6 +226,8 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
|
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
|
||||||
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
|
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
|
||||||
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
|
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
|
||||||
|
Add(Intrinsic.Arm64MrsFpcr, new IntrinsicInfo(0xd53b4400u, IntrinsicType.GetRegister));
|
||||||
|
Add(Intrinsic.Arm64MsrFpcr, new IntrinsicInfo(0xd51b4400u, IntrinsicType.SetRegister));
|
||||||
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
|
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
|
||||||
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
|
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
|
||||||
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));
|
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));
|
||||||
|
@@ -268,11 +268,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
Add(X86Instruction.Vfmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
Add(X86Instruction.Vfnmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
@@ -249,10 +249,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
case IntrinsicType.Mxcsr:
|
case IntrinsicType.Mxcsr:
|
||||||
{
|
{
|
||||||
Operand offset = operation.GetSource(0);
|
Operand offset = operation.GetSource(0);
|
||||||
Operand bits = operation.GetSource(1);
|
|
||||||
|
|
||||||
Debug.Assert(offset.Kind == OperandKind.Constant && bits.Kind == OperandKind.Constant);
|
Debug.Assert(offset.Kind == OperandKind.Constant);
|
||||||
Debug.Assert(offset.Type == OperandType.I32 && bits.Type == OperandType.I32);
|
Debug.Assert(offset.Type == OperandType.I32);
|
||||||
|
|
||||||
int offs = offset.AsInt32() + context.CallArgsRegionSize;
|
int offs = offset.AsInt32() + context.CallArgsRegionSize;
|
||||||
|
|
||||||
@@ -261,20 +260,22 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding);
|
Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding);
|
||||||
|
|
||||||
context.Assembler.Stmxcsr(memOp);
|
if (operation.Intrinsic == Intrinsic.X86Ldmxcsr)
|
||||||
|
|
||||||
if (operation.Intrinsic == Intrinsic.X86Mxcsrmb)
|
|
||||||
{
|
{
|
||||||
context.Assembler.Or(memOp, bits, OperandType.I32);
|
Operand bits = operation.GetSource(1);
|
||||||
}
|
Debug.Assert(bits.Type == OperandType.I32);
|
||||||
else /* if (intrinOp.Intrinsic == Intrinsic.X86Mxcsrub) */
|
|
||||||
{
|
|
||||||
Operand notBits = Const(~bits.AsInt32());
|
|
||||||
|
|
||||||
context.Assembler.And(memOp, notBits, OperandType.I32);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
context.Assembler.Mov(memOp, bits, OperandType.I32);
|
||||||
context.Assembler.Ldmxcsr(memOp);
|
context.Assembler.Ldmxcsr(memOp);
|
||||||
|
}
|
||||||
|
else if (operation.Intrinsic == Intrinsic.X86Stmxcsr)
|
||||||
|
{
|
||||||
|
Operand dest = operation.Destination;
|
||||||
|
Debug.Assert(dest.Type == OperandType.I32);
|
||||||
|
|
||||||
|
context.Assembler.Stmxcsr(memOp);
|
||||||
|
context.Assembler.Mov(dest, memOp, OperandType.I32);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
|
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
|
||||||
|
Add(Intrinsic.X86Ldmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr));
|
||||||
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
|
||||||
@@ -75,8 +76,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
|
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits.
|
|
||||||
Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits.
|
|
||||||
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
|
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
|
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
|
||||||
@@ -160,6 +159,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
|
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
|
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
|
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
|
||||||
|
Add(Intrinsic.X86Stmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr));
|
||||||
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
|
||||||
@@ -170,11 +170,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
|
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
|
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
|
||||||
|
Add(Intrinsic.X86Vfmadd231pd, new IntrinsicInfo(X86Instruction.Vfmadd231pd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
|
||||||
|
Add(Intrinsic.X86Vfnmadd231pd, new IntrinsicInfo(X86Instruction.Vfnmadd231pd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
||||||
|
15
ARMeilleure/CodeGen/X86/Mxcsr.cs
Normal file
15
ARMeilleure/CodeGen/X86/Mxcsr.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ARMeilleure.CodeGen.X86
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum Mxcsr
|
||||||
|
{
|
||||||
|
Ftz = 1 << 15, // Flush To Zero.
|
||||||
|
Rhi = 1 << 14, // Round Mode high bit.
|
||||||
|
Rlo = 1 << 13, // Round Mode low bit.
|
||||||
|
Um = 1 << 11, // Underflow Mask.
|
||||||
|
Dm = 1 << 8, // Denormal Mask.
|
||||||
|
Daz = 1 << 6 // Denormals Are Zero.
|
||||||
|
}
|
||||||
|
}
|
@@ -120,12 +120,18 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Instruction.Extended:
|
case Instruction.Extended:
|
||||||
if (node.Intrinsic == Intrinsic.X86Mxcsrmb || node.Intrinsic == Intrinsic.X86Mxcsrub)
|
if (node.Intrinsic == Intrinsic.X86Ldmxcsr)
|
||||||
{
|
{
|
||||||
int stackOffset = stackAlloc.Allocate(OperandType.I32);
|
int stackOffset = stackAlloc.Allocate(OperandType.I32);
|
||||||
|
|
||||||
node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) });
|
node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) });
|
||||||
}
|
}
|
||||||
|
else if (node.Intrinsic == Intrinsic.X86Stmxcsr)
|
||||||
|
{
|
||||||
|
int stackOffset = stackAlloc.Allocate(OperandType.I32);
|
||||||
|
|
||||||
|
node.SetSources(new Operand[] { Const(stackOffset) });
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -208,11 +208,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Vblendvps,
|
Vblendvps,
|
||||||
Vcvtph2ps,
|
Vcvtph2ps,
|
||||||
Vcvtps2ph,
|
Vcvtps2ph,
|
||||||
|
Vfmadd231pd,
|
||||||
Vfmadd231ps,
|
Vfmadd231ps,
|
||||||
Vfmadd231sd,
|
Vfmadd231sd,
|
||||||
Vfmadd231ss,
|
Vfmadd231ss,
|
||||||
Vfmsub231sd,
|
Vfmsub231sd,
|
||||||
Vfmsub231ss,
|
Vfmsub231ss,
|
||||||
|
Vfnmadd231pd,
|
||||||
Vfnmadd231ps,
|
Vfnmadd231ps,
|
||||||
Vfnmadd231sd,
|
Vfnmadd231sd,
|
||||||
Vfnmadd231ss,
|
Vfnmadd231ss,
|
||||||
|
@@ -108,6 +108,13 @@ namespace ARMeilleure.Decoders
|
|||||||
SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create);
|
SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create);
|
||||||
SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
||||||
SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
||||||
|
SetA64("11010101000000110010000011011111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("11010101000000110010000011111111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("110101010000001100100001xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("1101010100000011001000100xx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("1101010100000011001000101>>11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("110101010000001100100011xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("11010101000000110010>>xxxxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create);
|
SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create);
|
||||||
SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create);
|
SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create);
|
||||||
SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create);
|
SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create);
|
||||||
|
@@ -614,8 +614,6 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitSse2VectorPairwiseOpF(context, (op1, op2) =>
|
EmitSse2VectorPairwiseOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
@@ -623,7 +621,6 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
return context.AddIntrinsic(addInst, op1, op2);
|
return context.AddIntrinsic(addInst, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -696,17 +693,33 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@@ -729,11 +742,8 @@ namespace ARMeilleure.Instructions
|
|||||||
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
}, scalar: true, op1, op2);
|
|
||||||
}, scalar: true);
|
}, scalar: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -754,11 +764,8 @@ namespace ARMeilleure.Instructions
|
|||||||
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false);
|
}, scalar: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -885,12 +892,9 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitSse2VectorPairwiseOpF(context, (op1, op2) =>
|
EmitSse2VectorPairwiseOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -913,12 +917,9 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitSse2VectorAcrossVectorOpF(context, (op1, op2) =>
|
EmitSse2VectorAcrossVectorOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -939,11 +940,8 @@ namespace ARMeilleure.Instructions
|
|||||||
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
}, scalar: true, op1, op2);
|
|
||||||
}, scalar: true);
|
}, scalar: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -964,11 +962,8 @@ namespace ARMeilleure.Instructions
|
|||||||
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
else if (Optimizations.FastFP && Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false);
|
}, scalar: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1095,12 +1090,9 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitSse2VectorPairwiseOpF(context, (op1, op2) =>
|
EmitSse2VectorPairwiseOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1123,12 +1115,9 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitSse2VectorAcrossVectorOpF(context, (op1, op2) =>
|
EmitSse2VectorAcrossVectorOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1146,6 +1135,37 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe);
|
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe);
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||||
|
|
||||||
|
Operand d = GetVec(op.Rd);
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
if (sizeF == 0)
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
|
}
|
||||||
|
else /* if (sizeF == 1) */
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 1;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
||||||
@@ -1171,11 +1191,19 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@@ -1186,9 +1214,15 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
else /* if (sizeF == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@@ -1224,8 +1258,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@@ -1240,8 +1281,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@@ -1261,6 +1309,37 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe);
|
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe);
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||||
|
|
||||||
|
Operand d = GetVec(op.Rd);
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
if (sizeF == 0)
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
|
}
|
||||||
|
else /* if (sizeF == 1) */
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 1;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
||||||
@@ -1286,11 +1365,19 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@@ -1301,9 +1388,15 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
else /* if (sizeF == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@@ -1339,8 +1432,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@@ -1355,8 +1455,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@@ -1385,17 +1492,33 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@@ -1669,25 +1792,39 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0f);
|
Operand mask = X86GetScalar(context, -0f);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
|
{
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0d);
|
Operand mask = X86GetScalar(context, -0d);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@@ -1716,25 +1853,39 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0f);
|
Operand mask = X86GetScalar(context, -0f);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
|
{
|
||||||
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0d);
|
Operand mask = X86GetScalar(context, -0d);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
|
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@@ -1830,13 +1981,22 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, 2f);
|
Operand mask = X86GetScalar(context, 2f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
||||||
@@ -1845,9 +2005,16 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, 2d);
|
Operand mask = X86GetScalar(context, 2d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
||||||
@@ -1877,14 +2044,23 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetAllElements(context, 2f);
|
Operand mask = X86GetAllElements(context, 2f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@@ -1897,10 +2073,17 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Operand mask = X86GetAllElements(context, 2d);
|
Operand mask = X86GetAllElements(context, 2d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), res);
|
context.Copy(GetVec(op.Rd), res);
|
||||||
}
|
}
|
||||||
@@ -2113,21 +2296,33 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void Frintx_S(ArmEmitterContext context)
|
public static void Frintx_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
// TODO Arm64: Fast path. Should we set host FPCR?
|
if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintxS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
EmitScalarUnaryOpF(context, (op1) =>
|
EmitScalarUnaryOpF(context, (op1) =>
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
return EmitRoundByRMode(context, op1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Frintx_V(ArmEmitterContext context)
|
public static void Frintx_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
// TODO Arm64: Fast path. Should we set host FPCR?
|
if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintxV);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
EmitVectorUnaryOpF(context, (op1) =>
|
EmitVectorUnaryOpF(context, (op1) =>
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
return EmitRoundByRMode(context, op1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Frintz_S(ArmEmitterContext context)
|
public static void Frintz_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
@@ -2237,15 +2432,24 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand maskHalf = X86GetScalar(context, 0.5f);
|
Operand maskHalf = X86GetScalar(context, 0.5f);
|
||||||
Operand maskThree = X86GetScalar(context, 3f);
|
Operand maskThree = X86GetScalar(context, 3f);
|
||||||
Operand maskOneHalf = X86GetScalar(context, 1.5f);
|
Operand maskOneHalf = X86GetScalar(context, 1.5f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res);
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
||||||
|
|
||||||
@@ -2257,9 +2461,16 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand maskThree = X86GetScalar(context, 3d);
|
Operand maskThree = X86GetScalar(context, 3d);
|
||||||
Operand maskOneHalf = X86GetScalar(context, 1.5d);
|
Operand maskOneHalf = X86GetScalar(context, 1.5d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res);
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
||||||
|
|
||||||
@@ -2290,15 +2501,24 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand maskHalf = X86GetAllElements(context, 0.5f);
|
Operand maskHalf = X86GetAllElements(context, 0.5f);
|
||||||
Operand maskThree = X86GetAllElements(context, 3f);
|
Operand maskThree = X86GetAllElements(context, 3f);
|
||||||
Operand maskOneHalf = X86GetAllElements(context, 1.5f);
|
Operand maskOneHalf = X86GetAllElements(context, 1.5f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res);
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
||||||
|
|
||||||
@@ -2315,9 +2535,16 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand maskThree = X86GetAllElements(context, 3d);
|
Operand maskThree = X86GetAllElements(context, 3d);
|
||||||
Operand maskOneHalf = X86GetAllElements(context, 1.5d);
|
Operand maskOneHalf = X86GetAllElements(context, 1.5d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res);
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res);
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
||||||
|
|
||||||
@@ -4728,53 +4955,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand EmitSseOrAvxHandleFzModeOpF(
|
|
||||||
ArmEmitterContext context,
|
|
||||||
Func2I emit,
|
|
||||||
bool scalar,
|
|
||||||
Operand n = default,
|
|
||||||
Operand m = default)
|
|
||||||
{
|
|
||||||
Operand nCopy = n == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rn)) : n;
|
|
||||||
Operand mCopy = m == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rm)) : m;
|
|
||||||
|
|
||||||
EmitSseOrAvxEnterFtzAndDazModesOpF(context, out Operand isTrue);
|
|
||||||
|
|
||||||
Operand res = emit(nCopy, mCopy);
|
|
||||||
|
|
||||||
EmitSseOrAvxExitFtzAndDazModesOpF(context, isTrue);
|
|
||||||
|
|
||||||
if (n != default || m != default)
|
|
||||||
{
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sizeF = ((IOpCodeSimd)context.CurrOp).Size & 1;
|
|
||||||
|
|
||||||
if (sizeF == 0)
|
|
||||||
{
|
|
||||||
if (scalar)
|
|
||||||
{
|
|
||||||
res = context.VectorZeroUpper96(res);
|
|
||||||
}
|
|
||||||
else if (((OpCodeSimdReg)context.CurrOp).RegisterSize == RegisterSize.Simd64)
|
|
||||||
{
|
|
||||||
res = context.VectorZeroUpper64(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* if (sizeF == 1) */
|
|
||||||
{
|
|
||||||
if (scalar)
|
|
||||||
{
|
|
||||||
res = context.VectorZeroUpper64(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rd), res);
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax)
|
private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax)
|
||||||
{
|
{
|
||||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||||
@@ -4833,11 +5013,8 @@ namespace ARMeilleure.Instructions
|
|||||||
mCopy = context.AddIntrinsic(Intrinsic.X86Blendvps, mCopy, negInfMask, mMask);
|
mCopy = context.AddIntrinsic(Intrinsic.X86Blendvps, mCopy, negInfMask, mMask);
|
||||||
|
|
||||||
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
||||||
}, scalar: scalar, op1, op2);
|
|
||||||
}, scalar: scalar, nCopy, mCopy);
|
}, scalar: scalar, nCopy, mCopy);
|
||||||
|
|
||||||
if (n != default || m != default)
|
if (n != default || m != default)
|
||||||
@@ -4871,11 +5048,8 @@ namespace ARMeilleure.Instructions
|
|||||||
mCopy = context.AddIntrinsic(Intrinsic.X86Blendvpd, mCopy, negInfMask, mMask);
|
mCopy = context.AddIntrinsic(Intrinsic.X86Blendvpd, mCopy, negInfMask, mMask);
|
||||||
|
|
||||||
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
|
||||||
{
|
{
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
||||||
}, scalar: scalar, op1, op2);
|
|
||||||
}, scalar: scalar, nCopy, mCopy);
|
}, scalar: scalar, nCopy, mCopy);
|
||||||
|
|
||||||
if (n != default || m != default)
|
if (n != default || m != default)
|
||||||
|
@@ -356,9 +356,11 @@ namespace ARMeilleure.Instructions
|
|||||||
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
||||||
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(method, src);
|
Operand res = context.Call(method, src);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
InsertScalar16(context, op.Vd, op.T, res);
|
InsertScalar16(context, op.Vd, op.T, res);
|
||||||
}
|
}
|
||||||
@@ -372,9 +374,11 @@ namespace ARMeilleure.Instructions
|
|||||||
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
||||||
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(method, src);
|
Operand res = context.Call(method, src);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, res);
|
InsertScalar(context, op.Vd, res);
|
||||||
}
|
}
|
||||||
@@ -541,12 +545,19 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
// VRINTX (floating-point).
|
// VRINTX (floating-point).
|
||||||
public static void Vrintx_S(ArmEmitterContext context)
|
public static void Vrintx_S(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintxS);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF32(context, (op1) =>
|
EmitScalarUnaryOpF32(context, (op1) =>
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
return EmitRoundByRMode(context, op1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
||||||
{
|
{
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using ARMeilleure.CodeGen.X86;
|
||||||
using ARMeilleure.Decoders;
|
using ARMeilleure.Decoders;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
@@ -158,6 +159,75 @@ namespace ARMeilleure.Instructions
|
|||||||
};
|
};
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public static void EnterArmFpMode(EmitterContext context, Func<FPState, Operand> getFpFlag)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse2)
|
||||||
|
{
|
||||||
|
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
|
||||||
|
|
||||||
|
Operand fzTrue = getFpFlag(FPState.FzFlag);
|
||||||
|
Operand r0True = getFpFlag(FPState.RMode0Flag);
|
||||||
|
Operand r1True = getFpFlag(FPState.RMode1Flag);
|
||||||
|
|
||||||
|
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo)));
|
||||||
|
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(fzTrue, Const((int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Um | Mxcsr.Dm)), Const(0)));
|
||||||
|
|
||||||
|
// X86 round modes in order: nearest, negative, positive, zero
|
||||||
|
// ARM round modes in order: nearest, positive, negative, zero
|
||||||
|
// Read the bits backwards to correct this.
|
||||||
|
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r0True, Const((int)Mxcsr.Rhi), Const(0)));
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r1True, Const((int)Mxcsr.Rlo), Const(0)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
|
||||||
|
|
||||||
|
Operand fzTrue = getFpFlag(FPState.FzFlag);
|
||||||
|
Operand r0True = getFpFlag(FPState.RMode0Flag);
|
||||||
|
Operand r1True = getFpFlag(FPState.RMode1Flag);
|
||||||
|
|
||||||
|
fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1)));
|
||||||
|
|
||||||
|
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(fzTrue, Const((int)FPCR.Fz), Const(0)));
|
||||||
|
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r0True, Const((int)FPCR.RMode0), Const(0)));
|
||||||
|
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r1True, Const((int)FPCR.RMode1), Const(0)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
|
||||||
|
|
||||||
|
// TODO: Restore FPSR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ExitArmFpMode(EmitterContext context, Action<FPState, Operand> setFpFlag)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse2)
|
||||||
|
{
|
||||||
|
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
|
||||||
|
|
||||||
|
// Unset round mode (to nearest) and ftz.
|
||||||
|
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
|
||||||
|
|
||||||
|
// Status flags would be stored here if they were used.
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
|
||||||
|
|
||||||
|
// Unset round mode (to nearest) and fz.
|
||||||
|
fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
|
||||||
|
|
||||||
|
// TODO: Store FPSR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetImmShl(OpCodeSimdShImm op)
|
public static int GetImmShl(OpCodeSimdShImm op)
|
||||||
{
|
{
|
||||||
return op.Imm - (8 << op.Size);
|
return op.Imm - (8 << op.Size);
|
||||||
@@ -465,9 +535,11 @@ namespace ARMeilleure.Instructions
|
|||||||
? typeof(SoftFloat32).GetMethod(name)
|
? typeof(SoftFloat32).GetMethod(name)
|
||||||
: typeof(SoftFloat64).GetMethod(name);
|
: typeof(SoftFloat64).GetMethod(name);
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(info, callArgs);
|
Operand res = context.Call(info, callArgs);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -1358,39 +1430,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum Mxcsr
|
|
||||||
{
|
|
||||||
Ftz = 1 << 15, // Flush To Zero.
|
|
||||||
Um = 1 << 11, // Underflow Mask.
|
|
||||||
Dm = 1 << 8, // Denormal Mask.
|
|
||||||
Daz = 1 << 6 // Denormals Are Zero.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSseOrAvxEnterFtzAndDazModesOpF(ArmEmitterContext context, out Operand isTrue)
|
|
||||||
{
|
|
||||||
isTrue = GetFpFlag(FPState.FzFlag);
|
|
||||||
|
|
||||||
Operand lblTrue = Label();
|
|
||||||
context.BranchIfFalse(lblTrue, isTrue);
|
|
||||||
|
|
||||||
context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrmb, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm | Mxcsr.Daz)));
|
|
||||||
|
|
||||||
context.MarkLabel(lblTrue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default)
|
|
||||||
{
|
|
||||||
isTrue = isTrue == default ? GetFpFlag(FPState.FzFlag) : isTrue;
|
|
||||||
|
|
||||||
Operand lblTrue = Label();
|
|
||||||
context.BranchIfFalse(lblTrue, isTrue);
|
|
||||||
|
|
||||||
context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrub, Const((int)(Mxcsr.Ftz | Mxcsr.Daz)));
|
|
||||||
|
|
||||||
context.MarkLabel(lblTrue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CmpCondition
|
public enum CmpCondition
|
||||||
{
|
{
|
||||||
// Legacy Sse.
|
// Legacy Sse.
|
||||||
|
@@ -1197,9 +1197,11 @@ namespace ARMeilleure.Instructions
|
|||||||
Array.Resize(ref callArgs, callArgs.Length + 1);
|
Array.Resize(ref callArgs, callArgs.Length + 1);
|
||||||
callArgs[callArgs.Length - 1] = Const(1);
|
callArgs[callArgs.Length - 1] = Const(1);
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(info, callArgs);
|
Operand res = context.Call(info, callArgs);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@@ -33,8 +33,8 @@ namespace ARMeilleure.Instructions
|
|||||||
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
||||||
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
|
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
|
||||||
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
|
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return;
|
||||||
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
|
case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return;
|
||||||
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
||||||
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
||||||
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
||||||
@@ -49,19 +49,15 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (GetPackedId(op))
|
switch (GetPackedId(op))
|
||||||
{
|
{
|
||||||
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
||||||
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
|
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
|
||||||
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
|
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
|
case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Call(info, GetIntOrZR(context, op.Rt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Nop(ArmEmitterContext context)
|
public static void Nop(ArmEmitterContext context)
|
||||||
@@ -165,6 +161,28 @@ namespace ARMeilleure.Instructions
|
|||||||
SetIntOrZR(context, op.Rt, fpsr);
|
SetIntOrZR(context, op.Rt, fpsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitGetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
|
||||||
|
|
||||||
|
SetIntOrZR(context, op.Rt, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitGetTpidrroEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
|
||||||
|
|
||||||
|
SetIntOrZR(context, op.Rt, result);
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitSetNzcv(ArmEmitterContext context)
|
private static void EmitSetNzcv(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
@@ -192,6 +210,8 @@ namespace ARMeilleure.Instructions
|
|||||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
|
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.UpdateArmFpMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitSetFpsr(ArmEmitterContext context)
|
private static void EmitSetFpsr(ArmEmitterContext context)
|
||||||
@@ -210,6 +230,19 @@ namespace ARMeilleure.Instructions
|
|||||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
|
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.UpdateArmFpMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand value = GetIntOrZR(context, op.Rt);
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,8 +23,6 @@ namespace ARMeilleure.Instructions
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (op.CRn)
|
switch (op.CRn)
|
||||||
{
|
{
|
||||||
case 13: // Process and Thread Info.
|
case 13: // Process and Thread Info.
|
||||||
@@ -36,14 +34,12 @@ namespace ARMeilleure.Instructions
|
|||||||
switch (op.Opc2)
|
switch (op.Opc2)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break;
|
EmitSetTpidrEl0(context); return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
switch (op.CRm) // Cache and Memory barrier.
|
switch (op.CRm) // Cache and Memory barrier.
|
||||||
{
|
{
|
||||||
@@ -64,8 +60,6 @@ namespace ARMeilleure.Instructions
|
|||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Call(info, GetIntA32(context, op.Rt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Mrc(ArmEmitterContext context)
|
public static void Mrc(ArmEmitterContext context)
|
||||||
@@ -79,7 +73,7 @@ namespace ARMeilleure.Instructions
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodInfo info;
|
Operand result;
|
||||||
|
|
||||||
switch (op.CRn)
|
switch (op.CRn)
|
||||||
{
|
{
|
||||||
@@ -92,10 +86,10 @@ namespace ARMeilleure.Instructions
|
|||||||
switch (op.Opc2)
|
switch (op.Opc2)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break;
|
result = EmitGetTpidrEl0(context); break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break;
|
result = EmitGetTpidrroEl0(context); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||||
@@ -110,13 +104,13 @@ namespace ARMeilleure.Instructions
|
|||||||
if (op.Rt == RegisterAlias.Aarch32Pc)
|
if (op.Rt == RegisterAlias.Aarch32Pc)
|
||||||
{
|
{
|
||||||
// Special behavior: copy NZCV flags into APSR.
|
// Special behavior: copy NZCV flags into APSR.
|
||||||
EmitSetNzcv(context, context.Call(info));
|
EmitSetNzcv(context, result);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetIntA32(context, op.Rt, context.Call(info));
|
SetIntA32(context, op.Rt, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,6 +315,37 @@ namespace ARMeilleure.Instructions
|
|||||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
|
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.UpdateArmFpMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitGetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitGetTpidrroEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||||
|
|
||||||
|
Operand value = GetIntA32(context, op.Rt);
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,26 +72,6 @@ namespace ARMeilleure.Instructions
|
|||||||
return (ulong)GetContext().DczidEl0;
|
return (ulong)GetContext().DczidEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong GetTpidrEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().TpidrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetTpidrEl032()
|
|
||||||
{
|
|
||||||
return (uint)GetContext().TpidrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetTpidrroEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().TpidrroEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetTpidr32()
|
|
||||||
{
|
|
||||||
return (uint)GetContext().TpidrroEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetCntfrqEl0()
|
public static ulong GetCntfrqEl0()
|
||||||
{
|
{
|
||||||
return GetContext().CntfrqEl0;
|
return GetContext().CntfrqEl0;
|
||||||
@@ -106,16 +86,6 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return GetContext().CntvctEl0;
|
return GetContext().CntvctEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetTpidrEl0(ulong value)
|
|
||||||
{
|
|
||||||
GetContext().TpidrEl0 = (long)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetTpidrEl032(uint value)
|
|
||||||
{
|
|
||||||
GetContext().TpidrEl0 = (long)value;
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Read"
|
#region "Read"
|
||||||
|
@@ -53,6 +53,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Haddpd,
|
X86Haddpd,
|
||||||
X86Haddps,
|
X86Haddps,
|
||||||
X86Insertps,
|
X86Insertps,
|
||||||
|
X86Ldmxcsr,
|
||||||
X86Maxpd,
|
X86Maxpd,
|
||||||
X86Maxps,
|
X86Maxps,
|
||||||
X86Maxsd,
|
X86Maxsd,
|
||||||
@@ -68,8 +69,6 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Mulps,
|
X86Mulps,
|
||||||
X86Mulsd,
|
X86Mulsd,
|
||||||
X86Mulss,
|
X86Mulss,
|
||||||
X86Mxcsrmb,
|
|
||||||
X86Mxcsrub,
|
|
||||||
X86Paddb,
|
X86Paddb,
|
||||||
X86Paddd,
|
X86Paddd,
|
||||||
X86Paddq,
|
X86Paddq,
|
||||||
@@ -153,6 +152,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Sqrtps,
|
X86Sqrtps,
|
||||||
X86Sqrtsd,
|
X86Sqrtsd,
|
||||||
X86Sqrtss,
|
X86Sqrtss,
|
||||||
|
X86Stmxcsr,
|
||||||
X86Subpd,
|
X86Subpd,
|
||||||
X86Subps,
|
X86Subps,
|
||||||
X86Subsd,
|
X86Subsd,
|
||||||
@@ -163,11 +163,13 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Unpcklps,
|
X86Unpcklps,
|
||||||
X86Vcvtph2ps,
|
X86Vcvtph2ps,
|
||||||
X86Vcvtps2ph,
|
X86Vcvtps2ph,
|
||||||
|
X86Vfmadd231pd,
|
||||||
X86Vfmadd231ps,
|
X86Vfmadd231ps,
|
||||||
X86Vfmadd231sd,
|
X86Vfmadd231sd,
|
||||||
X86Vfmadd231ss,
|
X86Vfmadd231ss,
|
||||||
X86Vfmsub231sd,
|
X86Vfmsub231sd,
|
||||||
X86Vfmsub231ss,
|
X86Vfmsub231ss,
|
||||||
|
X86Vfnmadd231pd,
|
||||||
X86Vfnmadd231ps,
|
X86Vfnmadd231ps,
|
||||||
X86Vfnmadd231sd,
|
X86Vfnmadd231sd,
|
||||||
X86Vfnmadd231ss,
|
X86Vfnmadd231ss,
|
||||||
@@ -394,6 +396,8 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
Arm64MlsVe,
|
Arm64MlsVe,
|
||||||
Arm64MlsV,
|
Arm64MlsV,
|
||||||
Arm64MoviV,
|
Arm64MoviV,
|
||||||
|
Arm64MrsFpcr,
|
||||||
|
Arm64MsrFpcr,
|
||||||
Arm64MrsFpsr,
|
Arm64MrsFpsr,
|
||||||
Arm64MsrFpsr,
|
Arm64MsrFpsr,
|
||||||
Arm64MulVe,
|
Arm64MulVe,
|
||||||
|
@@ -27,8 +27,17 @@ namespace ARMeilleure.State
|
|||||||
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
||||||
public ulong CntvctEl0 => CntpctEl0;
|
public ulong CntvctEl0 => CntpctEl0;
|
||||||
|
|
||||||
public long TpidrEl0 { get; set; }
|
public long TpidrEl0
|
||||||
public long TpidrroEl0 { get; set; }
|
{
|
||||||
|
get => _nativeContext.GetTpidrEl0();
|
||||||
|
set => _nativeContext.SetTpidrEl0(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long TpidrroEl0
|
||||||
|
{
|
||||||
|
get => _nativeContext.GetTpidrroEl0();
|
||||||
|
set => _nativeContext.SetTpidrroEl0(value);
|
||||||
|
}
|
||||||
|
|
||||||
public uint Pstate
|
public uint Pstate
|
||||||
{
|
{
|
||||||
|
@@ -13,6 +13,8 @@ namespace ARMeilleure.State
|
|||||||
public fixed ulong V[RegisterConsts.VecRegsCount * 2];
|
public fixed ulong V[RegisterConsts.VecRegsCount * 2];
|
||||||
public fixed uint Flags[RegisterConsts.FlagsCount];
|
public fixed uint Flags[RegisterConsts.FlagsCount];
|
||||||
public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
|
public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
|
||||||
|
public long TpidrEl0;
|
||||||
|
public long TpidrroEl0;
|
||||||
public int Counter;
|
public int Counter;
|
||||||
public ulong DispatchAddress;
|
public ulong DispatchAddress;
|
||||||
public ulong ExclusiveAddress;
|
public ulong ExclusiveAddress;
|
||||||
@@ -168,6 +170,12 @@ namespace ARMeilleure.State
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetTpidrEl0() => GetStorage().TpidrEl0;
|
||||||
|
public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value;
|
||||||
|
|
||||||
|
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
|
||||||
|
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
|
||||||
|
|
||||||
public int GetCounter() => GetStorage().Counter;
|
public int GetCounter() => GetStorage().Counter;
|
||||||
public void SetCounter(int value) => GetStorage().Counter = value;
|
public void SetCounter(int value) => GetStorage().Counter = value;
|
||||||
|
|
||||||
@@ -214,6 +222,16 @@ namespace ARMeilleure.State
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetTpidrEl0Offset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetTpidrroEl0Offset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetCounterOffset()
|
public static int GetCounterOffset()
|
||||||
{
|
{
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
||||||
|
@@ -188,6 +188,21 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnterArmFpMode()
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.EnterArmFpMode(this, InstEmitHelper.GetFpFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateArmFpMode()
|
||||||
|
{
|
||||||
|
EnterArmFpMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitArmFpMode()
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.ExitArmFpMode(this, (flag, value) => InstEmitHelper.SetFpFlag(this, flag, value));
|
||||||
|
}
|
||||||
|
|
||||||
public Operand TryGetComparisonResult(Condition condition)
|
public Operand TryGetComparisonResult(Condition condition)
|
||||||
{
|
{
|
||||||
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
|
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
|
||||||
|
@@ -105,17 +105,11 @@ namespace ARMeilleure.Translation
|
|||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
||||||
|
@@ -3,4 +3,5 @@
|
|||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
||||||
|
delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress);
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 4485; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 4661; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@@ -25,5 +25,10 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
return _func(context.NativeContextPtr);
|
return _func(context.NativeContextPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong Execute(WrapperFunction dispatcher, State.ExecutionContext context)
|
||||||
|
{
|
||||||
|
return dispatcher(context.NativeContextPtr, (ulong)FuncPointer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -183,7 +183,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Statistics.StartTimer();
|
Statistics.StartTimer();
|
||||||
|
|
||||||
ulong nextAddr = func.Execute(context);
|
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
Statistics.StopTimer(address);
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
||||||
|
|
||||||
address = func.Execute(context);
|
address = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
EnqueueForDeletion(address, func);
|
EnqueueForDeletion(address, func);
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ namespace ARMeilleure.Translation
|
|||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
private readonly Lazy<IntPtr> _dispatchStub;
|
private readonly Lazy<IntPtr> _dispatchStub;
|
||||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||||
|
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the dispatch stub.
|
/// Gets the dispatch stub.
|
||||||
@@ -64,6 +65,20 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the context wrapper function.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
||||||
|
public WrapperFunction ContextWrapper
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
return _contextWrapper.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||||
/// <see cref="Translator"/> instance.
|
/// <see cref="Translator"/> instance.
|
||||||
@@ -77,6 +92,7 @@ namespace ARMeilleure.Translation
|
|||||||
_translator = translator;
|
_translator = translator;
|
||||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||||
|
_contextWrapper = new(GenerateContextWrapper, isThreadSafe: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -202,6 +218,32 @@ namespace ARMeilleure.Translation
|
|||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emits code that syncs FP state before executing guest code, or returns it to normal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Emitter context for the method</param>
|
||||||
|
/// <param name="nativeContext">Pointer to the native context</param>
|
||||||
|
/// <param name="enter">True if entering guest code, false otherwise</param>
|
||||||
|
private void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter)
|
||||||
|
{
|
||||||
|
if (enter)
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.EnterArmFpMode(context, (flag) =>
|
||||||
|
{
|
||||||
|
Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
|
||||||
|
return context.Load(OperandType.I32, flagAddress);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.ExitArmFpMode(context, (flag, value) =>
|
||||||
|
{
|
||||||
|
Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
|
||||||
|
context.Store(flagAddress, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a <see cref="DispatchLoop"/> function.
|
/// Generates a <see cref="DispatchLoop"/> function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -221,6 +263,8 @@ namespace ARMeilleure.Translation
|
|||||||
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
||||||
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
||||||
|
|
||||||
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
|
||||||
context.MarkLabel(beginLbl);
|
context.MarkLabel(beginLbl);
|
||||||
context.Store(dispatchAddress, guestAddress);
|
context.Store(dispatchAddress, guestAddress);
|
||||||
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
||||||
@@ -229,6 +273,9 @@ namespace ARMeilleure.Translation
|
|||||||
context.Branch(beginLbl);
|
context.Branch(beginLbl);
|
||||||
|
|
||||||
context.MarkLabel(endLbl);
|
context.MarkLabel(endLbl);
|
||||||
|
|
||||||
|
EmitSyncFpContext(context, nativeContext, false);
|
||||||
|
|
||||||
context.Return();
|
context.Return();
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
var cfg = context.GetControlFlowGraph();
|
||||||
@@ -237,5 +284,29 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a <see cref="ContextWrapper"/> function.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see cref="ContextWrapper"/> function</returns>
|
||||||
|
private WrapperFunction GenerateContextWrapper()
|
||||||
|
{
|
||||||
|
var context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
|
||||||
|
|
||||||
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
Operand returnValue = context.Call(guestMethod, OperandType.I64, nativeContext);
|
||||||
|
EmitSyncFpContext(context, nativeContext, false);
|
||||||
|
|
||||||
|
context.Return(returnValue);
|
||||||
|
|
||||||
|
var cfg = context.GetControlFlowGraph();
|
||||||
|
var retType = OperandType.I64;
|
||||||
|
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
148
ARMeilleure/Translation/TranslatorTestMethods.cs
Normal file
148
ARMeilleure/Translation/TranslatorTestMethods.cs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
using ARMeilleure.CodeGen.X86;
|
||||||
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using ARMeilleure.State;
|
||||||
|
using ARMeilleure.Translation;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Translation
|
||||||
|
{
|
||||||
|
public static class TranslatorTestMethods
|
||||||
|
{
|
||||||
|
public delegate int FpFlagsPInvokeTest(IntPtr managedMethod);
|
||||||
|
|
||||||
|
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse2)
|
||||||
|
{
|
||||||
|
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
|
||||||
|
|
||||||
|
if (ftz)
|
||||||
|
{
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)Mxcsr.Ftz));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
|
||||||
|
|
||||||
|
if (ftz)
|
||||||
|
{
|
||||||
|
fpcr = context.BitwiseOr(fpcr, Const((int)FPCR.Fz));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fpcr = context.BitwiseAnd(fpcr, Const(~(int)FPCR.Fz));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand FpBitsToInt(EmitterContext context, Operand fp)
|
||||||
|
{
|
||||||
|
Operand vec = context.VectorInsert(context.VectorZero(), fp, 0);
|
||||||
|
return context.VectorExtract(OperandType.I32, vec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
|
||||||
|
{
|
||||||
|
EmitterContext context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand methodAddress = context.Copy(context.LoadArgument(OperandType.I64, 0));
|
||||||
|
|
||||||
|
// Verify that default dotnet fp state does not flush to zero.
|
||||||
|
// This is required for SoftFloat to function.
|
||||||
|
|
||||||
|
// Denormal + zero != 0
|
||||||
|
|
||||||
|
Operand denormal = ConstF(BitConverter.Int32BitsToSingle(1)); // 1.40129846432e-45
|
||||||
|
Operand zeroF = ConstF(0f);
|
||||||
|
Operand zero = Const(0);
|
||||||
|
|
||||||
|
Operand result = context.Add(zeroF, denormal);
|
||||||
|
|
||||||
|
// Must not be zero.
|
||||||
|
|
||||||
|
Operand correct1Label = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(correct1Label, context.ICompareEqual(FpBitsToInt(context, result), zero));
|
||||||
|
|
||||||
|
context.Return(Const(1));
|
||||||
|
|
||||||
|
context.MarkLabel(correct1Label);
|
||||||
|
|
||||||
|
// Set flush to zero flag. If unsupported by the backend, just return true.
|
||||||
|
|
||||||
|
if (!SetPlatformFtz(context, true))
|
||||||
|
{
|
||||||
|
context.Return(Const(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Denormal + zero == 0
|
||||||
|
|
||||||
|
Operand resultFz = context.Add(zeroF, denormal);
|
||||||
|
|
||||||
|
// Must equal zero.
|
||||||
|
|
||||||
|
Operand correct2Label = Label();
|
||||||
|
|
||||||
|
context.BranchIfTrue(correct2Label, context.ICompareEqual(FpBitsToInt(context, resultFz), zero));
|
||||||
|
|
||||||
|
SetPlatformFtz(context, false);
|
||||||
|
|
||||||
|
context.Return(Const(2));
|
||||||
|
|
||||||
|
context.MarkLabel(correct2Label);
|
||||||
|
|
||||||
|
// Call a managed method. This method should not change Fz state.
|
||||||
|
|
||||||
|
context.Call(methodAddress, OperandType.None);
|
||||||
|
|
||||||
|
// Denormal + zero == 0
|
||||||
|
|
||||||
|
Operand resultFz2 = context.Add(zeroF, denormal);
|
||||||
|
|
||||||
|
// Must equal zero.
|
||||||
|
|
||||||
|
Operand correct3Label = Label();
|
||||||
|
|
||||||
|
context.BranchIfTrue(correct3Label, context.ICompareEqual(FpBitsToInt(context, resultFz2), zero));
|
||||||
|
|
||||||
|
SetPlatformFtz(context, false);
|
||||||
|
|
||||||
|
context.Return(Const(3));
|
||||||
|
|
||||||
|
context.MarkLabel(correct3Label);
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
|
||||||
|
SetPlatformFtz(context, false);
|
||||||
|
|
||||||
|
context.Return(Const(0));
|
||||||
|
|
||||||
|
// Compile and return the function.
|
||||||
|
|
||||||
|
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||||
|
|
||||||
|
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||||
|
|
||||||
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,7 @@
|
|||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageVersion Include="DynamicData" Version="7.13.1" />
|
<PackageVersion Include="DynamicData" Version="7.13.5" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.29.0" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.1" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable.
|
As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable.
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@@ -11,6 +11,7 @@ using Ryujinx.Audio.Renderer.Server.Voice;
|
|||||||
using Ryujinx.Audio.Renderer.Utils;
|
using Ryujinx.Audio.Renderer.Utils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -149,12 +150,16 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
state.InUse = false;
|
state.InUse = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Memory<VoiceUpdateState>[] voiceUpdateStatesArray = ArrayPool<Memory<VoiceUpdateState>>.Shared.Rent(Constants.VoiceChannelCountMax);
|
||||||
|
|
||||||
|
Span<Memory<VoiceUpdateState>> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax);
|
||||||
|
|
||||||
// Start processing
|
// Start processing
|
||||||
for (int i = 0; i < context.GetCount(); i++)
|
for (int i = 0; i < context.GetCount(); i++)
|
||||||
{
|
{
|
||||||
VoiceInParameter parameter = parameters[i];
|
VoiceInParameter parameter = parameters[i];
|
||||||
|
|
||||||
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
|
voiceUpdateStates.Fill(Memory<VoiceUpdateState>.Empty);
|
||||||
|
|
||||||
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
|
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
|
||||||
|
|
||||||
@@ -197,6 +202,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<Memory<VoiceUpdateState>>.Shared.Return(voiceUpdateStatesArray);
|
||||||
|
|
||||||
int currentOutputSize = _output.Length;
|
int currentOutputSize = _output.Length;
|
||||||
|
|
||||||
OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount());
|
OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount());
|
||||||
|
@@ -378,7 +378,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
/// <param name="outStatus">The given user output.</param>
|
/// <param name="outStatus">The given user output.</param>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
||||||
public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates)
|
public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Sanity check in debug mode of the internal state
|
// Sanity check in debug mode of the internal state
|
||||||
@@ -424,7 +424,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
||||||
/// <param name="mapper">The mapper to use.</param>
|
/// <param name="mapper">The mapper to use.</param>
|
||||||
/// <param name="behaviourContext">The behaviour context.</param>
|
/// <param name="behaviourContext">The behaviour context.</param>
|
||||||
public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
|
public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
|
||||||
{
|
{
|
||||||
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
|
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
|
||||||
|
|
||||||
|
@@ -177,6 +177,8 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||||
|
|
||||||
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,17 +323,15 @@ namespace Ryujinx.Ava
|
|||||||
_viewModel.IsGameRunning = true;
|
_viewModel.IsGameRunning = true;
|
||||||
|
|
||||||
var activeProcess = Device.Processes.ActiveApplication;
|
var activeProcess = Device.Processes.ActiveApplication;
|
||||||
var nacp = activeProcess.ApplicationControlProperties;
|
|
||||||
int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage;
|
|
||||||
|
|
||||||
string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}";
|
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
|
||||||
string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}";
|
string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}";
|
||||||
string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})";
|
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
|
||||||
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
|
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
_viewModel.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
_viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
||||||
});
|
});
|
||||||
|
|
||||||
_viewModel.SetUIProgressHandlers(Device);
|
_viewModel.SetUIProgressHandlers(Device);
|
||||||
@@ -385,6 +385,11 @@ namespace Ryujinx.Ava
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs<string> e)
|
||||||
|
{
|
||||||
|
Device.Configuration.MultiplayerLanInterfaceId = e.NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
@@ -741,7 +746,8 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio,
|
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||||
ConfigurationState.Instance.System.AudioVolume,
|
ConfigurationState.Instance.System.AudioVolume,
|
||||||
ConfigurationState.Instance.System.UseHypervisor);
|
ConfigurationState.Instance.System.UseHypervisor,
|
||||||
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
|
||||||
|
|
||||||
Device = new Switch(configuration);
|
Device = new Switch(configuration);
|
||||||
}
|
}
|
||||||
|
@@ -429,6 +429,7 @@
|
|||||||
"DlcManagerEnableAllButton": "Enable All",
|
"DlcManagerEnableAllButton": "Enable All",
|
||||||
"DlcManagerDisableAllButton": "Disable All",
|
"DlcManagerDisableAllButton": "Disable All",
|
||||||
"MenuBarOptionsChangeLanguage": "Change Language",
|
"MenuBarOptionsChangeLanguage": "Change Language",
|
||||||
|
"MenuBarShowFileTypes": "Show File Types",
|
||||||
"CommonSort": "Sort",
|
"CommonSort": "Sort",
|
||||||
"CommonShowNames": "Show Names",
|
"CommonShowNames": "Show Names",
|
||||||
"CommonFavorite": "Favorite",
|
"CommonFavorite": "Favorite",
|
||||||
@@ -637,5 +638,8 @@
|
|||||||
"SmaaHigh": "SMAA High",
|
"SmaaHigh": "SMAA High",
|
||||||
"SmaaUltra": "SMAA Ultra",
|
"SmaaUltra": "SMAA Ultra",
|
||||||
"UserEditorTitle" : "Edit User",
|
"UserEditorTitle" : "Edit User",
|
||||||
"UserEditorTitleCreate" : "Create User"
|
"UserEditorTitleCreate" : "Create User",
|
||||||
|
"SettingsTabNetworkInterface": "Network Interface:",
|
||||||
|
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
||||||
|
"NetworkInterfaceDefault": "Default"
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ using LibHac.Tools.Fs;
|
|||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
@@ -152,24 +153,16 @@ namespace Ryujinx.Ava.Common
|
|||||||
string destination = await folderDialog.ShowAsync(_owner);
|
string destination = await folderDialog.ShowAsync(_owner);
|
||||||
var cancellationToken = new CancellationTokenSource();
|
var cancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
UpdateWaitWindow waitingDialog = new(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(destination))
|
if (!string.IsNullOrWhiteSpace(destination))
|
||||||
{
|
{
|
||||||
Thread extractorThread = new(() =>
|
Thread extractorThread = new(() =>
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(waitingDialog.Show);
|
||||||
{
|
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogCancel],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
|
|
||||||
|
|
||||||
if (result == UserResult.Cancel)
|
|
||||||
{
|
|
||||||
cancellationToken.Cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
@@ -222,6 +215,8 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
waitingDialog.Close();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -263,11 +258,15 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
waitingDialog.Close();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (resultCode.Value.IsSuccess())
|
else if (resultCode.Value.IsSuccess())
|
||||||
{
|
{
|
||||||
|
Dispatcher.UIThread.Post(waitingDialog.Close);
|
||||||
|
|
||||||
NotificationHelper.Show(
|
NotificationHelper.Show(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
||||||
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
|
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
|
||||||
@@ -284,6 +283,8 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
waitingDialog.Close();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -730,7 +730,7 @@ namespace Ryujinx.Modules
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
||||||
|
@@ -53,6 +53,8 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
bool opened = false;
|
bool opened = false;
|
||||||
|
|
||||||
|
_parent.Activate();
|
||||||
|
|
||||||
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
|
@@ -48,6 +48,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Focusable="True"
|
||||||
KeyUp="Message_KeyUp"
|
KeyUp="Message_KeyUp"
|
||||||
Text="{Binding Message}"
|
Text="{Binding Message}"
|
||||||
TextInput="Message_TextInput"
|
TextInput="Message_TextInput"
|
||||||
|
@@ -23,10 +23,11 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
private ContentDialog _host;
|
private ContentDialog _host;
|
||||||
|
|
||||||
public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder)
|
public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder, string message)
|
||||||
{
|
{
|
||||||
MainText = mainText;
|
MainText = mainText;
|
||||||
SecondaryText = secondaryText;
|
SecondaryText = secondaryText;
|
||||||
|
Message = message ?? "";
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
_placeholder = placeholder;
|
_placeholder = placeholder;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -44,6 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||||
|
{
|
||||||
|
// FIXME: This does not work. Might be a bug in Avalonia with DialogHost
|
||||||
|
// Currently focus will be redirected to the overlay window instead.
|
||||||
|
Input.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
public string Message { get; set; } = "";
|
public string Message { get; set; } = "";
|
||||||
public string MainText { get; set; } = "";
|
public string MainText { get; set; } = "";
|
||||||
public string SecondaryText { get; set; } = "";
|
public string SecondaryText { get; set; } = "";
|
||||||
@@ -54,31 +62,10 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
UserResult result = UserResult.Cancel;
|
UserResult result = UserResult.Cancel;
|
||||||
|
|
||||||
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText)
|
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
|
||||||
{
|
|
||||||
Message = args.InitialText ?? ""
|
|
||||||
};
|
|
||||||
|
|
||||||
string input = string.Empty;
|
string input = string.Empty;
|
||||||
|
|
||||||
var overlay = new ContentDialogOverlayWindow()
|
|
||||||
{
|
|
||||||
Height = window.Bounds.Height,
|
|
||||||
Width = window.Bounds.Width,
|
|
||||||
Position = window.PointToScreen(new Point())
|
|
||||||
};
|
|
||||||
|
|
||||||
window.PositionChanged += OverlayOnPositionChanged;
|
|
||||||
|
|
||||||
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
|
|
||||||
{
|
|
||||||
overlay.Position = window.PointToScreen(new Point());
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDialog = overlay.ContentDialog;
|
|
||||||
|
|
||||||
bool opened = false;
|
|
||||||
|
|
||||||
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
||||||
|
|
||||||
content._host = contentDialog;
|
content._host = contentDialog;
|
||||||
@@ -99,25 +86,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
};
|
};
|
||||||
contentDialog.Closed += handler;
|
contentDialog.Closed += handler;
|
||||||
|
|
||||||
overlay.Opened += OverlayOnActivated;
|
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||||
|
|
||||||
async void OverlayOnActivated(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (opened)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
opened = true;
|
|
||||||
|
|
||||||
overlay.Position = window.PointToScreen(new Point());
|
|
||||||
|
|
||||||
await contentDialog.ShowAsync(overlay);
|
|
||||||
contentDialog.Closed -= handler;
|
|
||||||
overlay.Close();
|
|
||||||
};
|
|
||||||
|
|
||||||
await overlay.ShowDialog(window);
|
|
||||||
|
|
||||||
return (result, input);
|
return (result, input);
|
||||||
}
|
}
|
||||||
|
@@ -1,32 +0,0 @@
|
|||||||
<UserControl
|
|
||||||
x:Class="Ryujinx.Ava.UI.Controls.InputDialog"
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Focusable="True">
|
|
||||||
<Grid
|
|
||||||
Margin="5,10,5,5"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Center">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock HorizontalAlignment="Center" Text="{Binding Message}" />
|
|
||||||
<TextBox
|
|
||||||
Grid.Row="1"
|
|
||||||
Width="300"
|
|
||||||
Margin="10"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
MaxLength="{Binding MaxLength}"
|
|
||||||
Text="{Binding Input, Mode=TwoWay}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="2"
|
|
||||||
Margin="5,5,5,10"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Text="{Binding SubMessage}" />
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
@@ -1,57 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using FluentAvalonia.UI.Controls;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
|
||||||
{
|
|
||||||
public partial class InputDialog : UserControl
|
|
||||||
{
|
|
||||||
public string Message { get; set; }
|
|
||||||
public string Input { get; set; }
|
|
||||||
public string SubMessage { get; set; }
|
|
||||||
|
|
||||||
public uint MaxLength { get; }
|
|
||||||
|
|
||||||
public InputDialog(string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
Input = input;
|
|
||||||
SubMessage = subMessage;
|
|
||||||
MaxLength = maxLength;
|
|
||||||
|
|
||||||
DataContext = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputDialog()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message,
|
|
||||||
string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
|
||||||
{
|
|
||||||
UserResult result = UserResult.Cancel;
|
|
||||||
|
|
||||||
InputDialog content = new InputDialog(message, input, subMessage, maxLength);
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
|
||||||
SecondaryButtonText = "",
|
|
||||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel],
|
|
||||||
Content = content,
|
|
||||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
|
||||||
{
|
|
||||||
result = UserResult.Ok;
|
|
||||||
input = content.Input;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
await contentDialog.ShowAsync();
|
|
||||||
|
|
||||||
return (result, input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +1,26 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
||||||
{
|
{
|
||||||
public partial class UpdateWaitWindow : StyleableWindow
|
public partial class UpdateWaitWindow : StyleableWindow
|
||||||
{
|
{
|
||||||
|
public UpdateWaitWindow(string primaryText, string secondaryText, CancellationTokenSource cancellationToken) : this(primaryText, secondaryText)
|
||||||
|
{
|
||||||
|
SystemDecorations = SystemDecorations.Full;
|
||||||
|
ShowInTaskbar = true;
|
||||||
|
|
||||||
|
Closing += (_, _) => cancellationToken.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
public UpdateWaitWindow(string primaryText, string secondaryText) : this()
|
public UpdateWaitWindow(string primaryText, string secondaryText) : this()
|
||||||
{
|
{
|
||||||
PrimaryText.Text = primaryText;
|
PrimaryText.Text = primaryText;
|
||||||
SecondaryText.Text = secondaryText;
|
SecondaryText.Text = secondaryText;
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
SystemDecorations = SystemDecorations.BorderOnly;
|
||||||
|
ShowInTaskbar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateWaitWindow()
|
public UpdateWaitWindow()
|
||||||
|
@@ -27,7 +27,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
string closeButton,
|
string closeButton,
|
||||||
UserResult primaryButtonResult = UserResult.Ok,
|
UserResult primaryButtonResult = UserResult.Ok,
|
||||||
ManualResetEvent deferResetEvent = null,
|
ManualResetEvent deferResetEvent = null,
|
||||||
Func<Window, Task> doWhileDeferred = null,
|
|
||||||
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
||||||
{
|
{
|
||||||
UserResult result = UserResult.None;
|
UserResult result = UserResult.None;
|
||||||
@@ -78,12 +77,11 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
int iconSymbol,
|
int iconSymbol,
|
||||||
UserResult primaryButtonResult = UserResult.Ok,
|
UserResult primaryButtonResult = UserResult.Ok,
|
||||||
ManualResetEvent deferResetEvent = null,
|
ManualResetEvent deferResetEvent = null,
|
||||||
Func<Window, Task> doWhileDeferred = null,
|
|
||||||
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
||||||
{
|
{
|
||||||
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
|
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
|
||||||
|
|
||||||
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction);
|
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async static Task<UserResult> ShowDeferredContentDialog(
|
public async static Task<UserResult> ShowDeferredContentDialog(
|
||||||
@@ -111,7 +109,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
iconSymbol,
|
iconSymbol,
|
||||||
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
|
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
|
||||||
deferResetEvent,
|
deferResetEvent,
|
||||||
doWhileDeferred,
|
|
||||||
DeferClose);
|
DeferClose);
|
||||||
|
|
||||||
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||||
@@ -236,11 +233,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
primaryButtonResult);
|
primaryButtonResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static UpdateWaitWindow CreateWaitingDialog(string mainText, string secondaryText)
|
|
||||||
{
|
|
||||||
return new(mainText, secondaryText);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
||||||
{
|
{
|
||||||
await ShowTextDialog(
|
await ShowTextDialog(
|
||||||
@@ -319,28 +311,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<string> CreateInputDialog(
|
|
||||||
string title,
|
|
||||||
string mainText,
|
|
||||||
string subText,
|
|
||||||
uint maxLength = int.MaxValue,
|
|
||||||
string input = "")
|
|
||||||
{
|
|
||||||
var result = await InputDialog.ShowInputDialog(
|
|
||||||
title,
|
|
||||||
mainText,
|
|
||||||
input,
|
|
||||||
subText,
|
|
||||||
maxLength);
|
|
||||||
|
|
||||||
if (result.Result == UserResult.Ok)
|
|
||||||
{
|
|
||||||
return result.Input;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
||||||
{
|
{
|
||||||
ContentDialogResult result;
|
ContentDialogResult result;
|
||||||
|
@@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui.Common.Models.Amiibo;
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
using System;
|
using System;
|
||||||
@@ -48,7 +49,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_httpClient = new HttpClient { Timeout = TimeSpan.FromMilliseconds(5000) };
|
|
||||||
|
_httpClient = new HttpClient
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(30)
|
||||||
|
};
|
||||||
|
|
||||||
LastScannedAmiiboId = lastScannedAmiiboId;
|
LastScannedAmiiboId = lastScannedAmiiboId;
|
||||||
TitleId = titleId;
|
TitleId = titleId;
|
||||||
|
|
||||||
@@ -89,9 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
_showAllAmiibo = value;
|
_showAllAmiibo = value;
|
||||||
|
|
||||||
#pragma warning disable 4014
|
|
||||||
ParseAmiiboData();
|
ParseAmiiboData();
|
||||||
#pragma warning restore 4014
|
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
@@ -203,8 +207,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
|
||||||
|
|
||||||
ShowInfoDialog();
|
ShowInfoDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,8 +375,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
|
||||||
|
|
||||||
ShowInfoDialog();
|
ShowInfoDialog();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -393,6 +401,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return amiiboJsonString;
|
return amiiboJsonString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
|
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
@@ -429,6 +439,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
|
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetAmiiboPreview()
|
private void ResetAmiiboPreview()
|
||||||
|
@@ -972,7 +972,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
|
UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
@@ -1336,6 +1336,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleFileType(string fileType)
|
||||||
|
{
|
||||||
|
_ = fileType switch
|
||||||
|
{
|
||||||
|
"NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
|
||||||
|
"PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
|
||||||
|
"XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
|
||||||
|
"NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
|
||||||
|
"NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
|
||||||
|
"NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(fileType),
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
LoadApplications();
|
||||||
|
}
|
||||||
|
|
||||||
public async void ManageProfiles()
|
public async void ManageProfiles()
|
||||||
{
|
{
|
||||||
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
||||||
|
@@ -23,6 +23,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
@@ -35,6 +36,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private readonly List<string> _validTzRegions;
|
private readonly List<string> _validTzRegions;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, string> _networkInterfaces;
|
||||||
|
|
||||||
private float _customResolutionScale;
|
private float _customResolutionScale;
|
||||||
private int _resolutionScale;
|
private int _resolutionScale;
|
||||||
private int _graphicsBackendMultithreadingIndex;
|
private int _graphicsBackendMultithreadingIndex;
|
||||||
@@ -50,6 +53,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public event Action CloseWindow;
|
public event Action CloseWindow;
|
||||||
public event Action SaveSettingsEvent;
|
public event Action SaveSettingsEvent;
|
||||||
|
private int _networkInterfaceIndex;
|
||||||
|
|
||||||
public int ResolutionScale
|
public int ResolutionScale
|
||||||
{
|
{
|
||||||
@@ -240,6 +244,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public AvaloniaList<string> GameDirectories { get; set; }
|
public AvaloniaList<string> GameDirectories { get; set; }
|
||||||
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
|
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
|
||||||
|
|
||||||
|
public AvaloniaList<string> NetworkInterfaceList
|
||||||
|
{
|
||||||
|
get => new AvaloniaList<string>(_networkInterfaces.Keys);
|
||||||
|
}
|
||||||
|
|
||||||
public KeyboardHotkeys KeyboardHotkeys
|
public KeyboardHotkeys KeyboardHotkeys
|
||||||
{
|
{
|
||||||
get => _keyboardHotkeys;
|
get => _keyboardHotkeys;
|
||||||
@@ -251,6 +260,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int NetworkInterfaceIndex
|
||||||
|
{
|
||||||
|
get => _networkInterfaceIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_networkInterfaceIndex = value != -1 ? value : 0;
|
||||||
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
@@ -267,8 +286,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
TimeZones = new AvaloniaList<TimeZone>();
|
TimeZones = new AvaloniaList<TimeZone>();
|
||||||
AvailableGpus = new ObservableCollection<ComboBoxItem>();
|
AvailableGpus = new ObservableCollection<ComboBoxItem>();
|
||||||
_validTzRegions = new List<string>();
|
_validTzRegions = new List<string>();
|
||||||
|
_networkInterfaces = new Dictionary<string, string>();
|
||||||
|
|
||||||
CheckSoundBackends();
|
CheckSoundBackends();
|
||||||
|
PopulateNetworkInterfaces();
|
||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
@@ -327,6 +348,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PopulateNetworkInterfaces()
|
||||||
|
{
|
||||||
|
_networkInterfaces.Clear();
|
||||||
|
_networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0");
|
||||||
|
|
||||||
|
foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||||
|
{
|
||||||
|
_networkInterfaces.Add(networkInterface.Name, networkInterface.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ValidateAndSetTimeZone(string location)
|
public void ValidateAndSetTimeZone(string location)
|
||||||
{
|
{
|
||||||
if (_validTzRegions.Contains(location))
|
if (_validTzRegions.Contains(location))
|
||||||
@@ -414,6 +446,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
||||||
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
||||||
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
||||||
|
|
||||||
|
NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings()
|
public void SaveSettings()
|
||||||
@@ -515,6 +549,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
||||||
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
||||||
|
|
||||||
|
config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];
|
||||||
|
|
||||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
@@ -78,6 +78,7 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
|
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
|
||||||
|
<MenuItem Name="ToggleFileTypesMenuItem" Header="{locale:Locale MenuBarShowFileTypes}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenSettings"
|
Click="OpenSettings"
|
||||||
|
@@ -11,6 +11,8 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
|
using Ryujinx.Ui.Common;
|
||||||
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -29,6 +31,30 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
ToggleFileTypesMenuItem.Items = GenerateToggleFileTypeItems();
|
||||||
|
ChangeLanguageMenuItem.Items = GenerateLanguageMenuItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CheckBox[] GenerateToggleFileTypeItems()
|
||||||
|
{
|
||||||
|
List<CheckBox> checkBoxes = new();
|
||||||
|
|
||||||
|
foreach (var item in Enum.GetValues(typeof (FileTypes)))
|
||||||
|
{
|
||||||
|
string fileName = Enum.GetName(typeof (FileTypes), item);
|
||||||
|
checkBoxes.Add(new CheckBox()
|
||||||
|
{
|
||||||
|
Content = $".{fileName}",
|
||||||
|
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
|
||||||
|
Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkBoxes.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem[] GenerateLanguageMenuItems()
|
||||||
|
{
|
||||||
List<MenuItem> menuItems = new();
|
List<MenuItem> menuItems = new();
|
||||||
|
|
||||||
string localePath = "Ryujinx.Ava/Assets/Locales";
|
string localePath = "Ryujinx.Ava/Assets/Locales";
|
||||||
@@ -61,7 +87,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
menuItems.Add(menuItem);
|
menuItems.Add(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeLanguageMenuItem.Items = menuItems.ToArray();
|
return menuItems.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
@@ -29,6 +29,17 @@
|
|||||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
||||||
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
Text="{locale:Locale SettingsTabNetworkInterface}"
|
||||||
|
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
|
||||||
|
Width="200" />
|
||||||
|
<ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}"
|
||||||
|
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Items="{Binding NetworkInterfaceList}"
|
||||||
|
Width="250" />
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
@@ -10,20 +10,16 @@
|
|||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow"
|
x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow"
|
||||||
Title="ContentDialogOverlayWindow"
|
Title="ContentDialogOverlayWindow"
|
||||||
Focusable="True">
|
Focusable="False">
|
||||||
<window:StyleableWindow.Styles>
|
<window:StyleableWindow.Styles>
|
||||||
<Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot">
|
<Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot">
|
||||||
<Setter Property="Background"
|
<Setter Property="Background"
|
||||||
Value="Transparent" />
|
Value="Transparent" />
|
||||||
</Style>
|
</Style>
|
||||||
</window:StyleableWindow.Styles>
|
</window:StyleableWindow.Styles>
|
||||||
<ContentControl
|
|
||||||
Focusable="False"
|
|
||||||
IsVisible="False"
|
|
||||||
KeyboardNavigation.IsTabStop="False">
|
|
||||||
<ui:ContentDialog Name="ContentDialog"
|
<ui:ContentDialog Name="ContentDialog"
|
||||||
IsPrimaryButtonEnabled="True"
|
IsPrimaryButtonEnabled="True"
|
||||||
IsSecondaryButtonEnabled="True"
|
IsSecondaryButtonEnabled="True"
|
||||||
IsVisible="False" />
|
IsVisible="False"
|
||||||
</ContentControl>
|
Focusable="True"/>
|
||||||
</window:StyleableWindow>
|
</window:StyleableWindow>
|
||||||
|
51
Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
Normal file
51
Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public sealed partial class ByteMemoryPool
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
|
||||||
|
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
|
||||||
|
/// with a length of the requested size.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
|
||||||
|
{
|
||||||
|
private byte[] _array;
|
||||||
|
private readonly int _length;
|
||||||
|
|
||||||
|
public ByteMemoryPoolBuffer(int length)
|
||||||
|
{
|
||||||
|
_array = ArrayPool<byte>.Shared.Rent(length);
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
|
||||||
|
/// </summary>
|
||||||
|
public Memory<byte> Memory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
byte[] array = _array;
|
||||||
|
|
||||||
|
ObjectDisposedException.ThrowIf(array is null, this);
|
||||||
|
|
||||||
|
return new Memory<byte>(array, 0, _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var array = Interlocked.Exchange(ref _array, null);
|
||||||
|
|
||||||
|
if (array != null)
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
Ryujinx.Common/Memory/ByteMemoryPool.cs
Normal file
108
Ryujinx.Common/Memory/ByteMemoryPool.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a pool of re-usable byte array instances.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class ByteMemoryPool
|
||||||
|
{
|
||||||
|
private static readonly ByteMemoryPool _shared = new ByteMemoryPool();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through
|
||||||
|
/// the <see cref="ByteMemoryPool.Shared"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
private ByteMemoryPool()
|
||||||
|
{
|
||||||
|
// No implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a shared <see cref="ByteMemoryPool"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public static ByteMemoryPool Shared => _shared;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the maximum buffer size supported by this pool.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxBufferSize => Array.MaxLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> Rent(long length)
|
||||||
|
=> RentImpl(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> Rent(ulong length)
|
||||||
|
=> RentImpl(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> Rent(int length)
|
||||||
|
=> RentImpl(length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> RentCleared(long length)
|
||||||
|
=> RentCleared(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> RentCleared(ulong length)
|
||||||
|
=> RentCleared(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> RentCleared(int length)
|
||||||
|
{
|
||||||
|
var buffer = RentImpl(length);
|
||||||
|
|
||||||
|
buffer.Memory.Span.Clear();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteMemoryPoolBuffer RentImpl(int length)
|
||||||
|
{
|
||||||
|
if ((uint)length > Array.MaxLength)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(length), length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ByteMemoryPoolBuffer(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -33,6 +33,11 @@ namespace Ryujinx.Common.Memory
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> GetSpanSafe(int size)
|
||||||
|
{
|
||||||
|
return GetSpan((int)Math.Min((uint)_input.Length, (uint)size));
|
||||||
|
}
|
||||||
|
|
||||||
public T ReadAt<T>(int offset) where T : unmanaged
|
public T ReadAt<T>(int offset) where T : unmanaged
|
||||||
{
|
{
|
||||||
return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0];
|
return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0];
|
||||||
|
66
Ryujinx.Common/Utilities/NetworkHelpers.cs
Normal file
66
Ryujinx.Common/Utilities/NetworkHelpers.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.Net.NetworkInformation;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
public static class NetworkHelpers
|
||||||
|
{
|
||||||
|
private static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(NetworkInterface adapter, bool isPreferred)
|
||||||
|
{
|
||||||
|
IPInterfaceProperties properties = adapter.GetIPProperties();
|
||||||
|
|
||||||
|
if (isPreferred || (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0))
|
||||||
|
{
|
||||||
|
foreach (UnicastIPAddressInformation info in properties.UnicastAddresses)
|
||||||
|
{
|
||||||
|
// Only accept an IPv4 address
|
||||||
|
if (info.Address.GetAddressBytes().Length == 4)
|
||||||
|
{
|
||||||
|
return (properties, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(string lanInterfaceId = "0")
|
||||||
|
{
|
||||||
|
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPInterfaceProperties targetProperties = null;
|
||||||
|
UnicastIPAddressInformation targetAddressInfo = null;
|
||||||
|
|
||||||
|
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||||
|
|
||||||
|
string guid = lanInterfaceId;
|
||||||
|
bool hasPreference = guid != "0";
|
||||||
|
|
||||||
|
foreach (NetworkInterface adapter in interfaces)
|
||||||
|
{
|
||||||
|
bool isPreferred = adapter.Id == guid;
|
||||||
|
|
||||||
|
// Ignore loopback and non IPv4 capable interface.
|
||||||
|
if (isPreferred || (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)))
|
||||||
|
{
|
||||||
|
(IPInterfaceProperties properties, UnicastIPAddressInformation info) = GetLocalInterface(adapter, isPreferred);
|
||||||
|
|
||||||
|
if (properties != null)
|
||||||
|
{
|
||||||
|
targetProperties = properties;
|
||||||
|
targetAddressInfo = info;
|
||||||
|
|
||||||
|
if (isPreferred || !hasPreference)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (targetProperties, targetAddressInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -35,7 +35,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly bool SupportsNonConstantTextureOffset;
|
public readonly bool SupportsNonConstantTextureOffset;
|
||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
public readonly bool SupportsTextureShadowLod;
|
public readonly bool SupportsTextureShadowLod;
|
||||||
public readonly bool SupportsViewportIndex;
|
public readonly bool SupportsViewportIndexVertexTessellation;
|
||||||
|
public readonly bool SupportsViewportMask;
|
||||||
public readonly bool SupportsViewportSwizzle;
|
public readonly bool SupportsViewportSwizzle;
|
||||||
public readonly bool SupportsIndirectParameters;
|
public readonly bool SupportsIndirectParameters;
|
||||||
|
|
||||||
@@ -48,6 +49,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly float MaximumSupportedAnisotropy;
|
public readonly float MaximumSupportedAnisotropy;
|
||||||
public readonly int StorageBufferOffsetAlignment;
|
public readonly int StorageBufferOffsetAlignment;
|
||||||
|
|
||||||
|
public readonly int GatherBiasPrecision;
|
||||||
|
|
||||||
public Capabilities(
|
public Capabilities(
|
||||||
TargetApi api,
|
TargetApi api,
|
||||||
string vendorName,
|
string vendorName,
|
||||||
@@ -78,7 +81,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
bool supportsNonConstantTextureOffset,
|
bool supportsNonConstantTextureOffset,
|
||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
bool supportsTextureShadowLod,
|
bool supportsTextureShadowLod,
|
||||||
bool supportsViewportIndex,
|
bool supportsViewportIndexVertexTessellation,
|
||||||
|
bool supportsViewportMask,
|
||||||
bool supportsViewportSwizzle,
|
bool supportsViewportSwizzle,
|
||||||
bool supportsIndirectParameters,
|
bool supportsIndirectParameters,
|
||||||
uint maximumUniformBuffersPerStage,
|
uint maximumUniformBuffersPerStage,
|
||||||
@@ -87,7 +91,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
uint maximumImagesPerStage,
|
uint maximumImagesPerStage,
|
||||||
int maximumComputeSharedMemorySize,
|
int maximumComputeSharedMemorySize,
|
||||||
float maximumSupportedAnisotropy,
|
float maximumSupportedAnisotropy,
|
||||||
int storageBufferOffsetAlignment)
|
int storageBufferOffsetAlignment,
|
||||||
|
int gatherBiasPrecision)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
VendorName = vendorName;
|
VendorName = vendorName;
|
||||||
@@ -118,7 +123,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||||
SupportsViewportIndex = supportsViewportIndex;
|
SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation;
|
||||||
|
SupportsViewportMask = supportsViewportMask;
|
||||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||||
SupportsIndirectParameters = supportsIndirectParameters;
|
SupportsIndirectParameters = supportsIndirectParameters;
|
||||||
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
||||||
@@ -128,6 +134,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
|
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
|
||||||
MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
|
MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
|
||||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||||
|
GatherBiasPrecision = gatherBiasPrecision;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Texture;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
@@ -32,6 +33,69 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
RemapEnable = 1 << 10
|
RemapEnable = 1 << 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Texture parameters for copy.
|
||||||
|
/// </summary>
|
||||||
|
private struct TextureParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Copy region X coordinate.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int RegionX;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy region Y coordinate.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int RegionY;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Offset from the base pointer of the data in memory.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int BaseOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bytes per pixel.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int Bpp;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the texture is linear. If false, the texture is block linear.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Linear;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pixel offset from XYZ coordinates calculator.
|
||||||
|
/// </summary>
|
||||||
|
public readonly OffsetCalculator Calculator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates texture parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="regionX">Copy region X coordinate</param>
|
||||||
|
/// <param name="regionY">Copy region Y coordinate</param>
|
||||||
|
/// <param name="baseOffset">Offset from the base pointer of the data in memory</param>
|
||||||
|
/// <param name="bpp">Bytes per pixel</param>
|
||||||
|
/// <param name="linear">Whether the texture is linear. If false, the texture is block linear</param>
|
||||||
|
/// <param name="calculator">Pixel offset from XYZ coordinates calculator</param>
|
||||||
|
public TextureParams(int regionX, int regionY, int baseOffset, int bpp, bool linear, OffsetCalculator calculator)
|
||||||
|
{
|
||||||
|
RegionX = regionX;
|
||||||
|
RegionY = regionY;
|
||||||
|
BaseOffset = baseOffset;
|
||||||
|
Bpp = bpp;
|
||||||
|
Linear = linear;
|
||||||
|
Calculator = calculator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)]
|
||||||
|
private struct UInt24
|
||||||
|
{
|
||||||
|
public byte Byte0;
|
||||||
|
public byte Byte1;
|
||||||
|
public byte Byte2;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the DMA copy engine class.
|
/// Creates a new instance of the DMA copy engine class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -154,8 +218,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
{
|
{
|
||||||
// Buffer to texture copy.
|
// Buffer to texture copy.
|
||||||
int componentSize = (int)_state.State.SetRemapComponentsComponentSize + 1;
|
int componentSize = (int)_state.State.SetRemapComponentsComponentSize + 1;
|
||||||
int srcBpp = remap ? ((int)_state.State.SetRemapComponentsNumSrcComponents + 1) * componentSize : 1;
|
int srcComponents = (int)_state.State.SetRemapComponentsNumSrcComponents + 1;
|
||||||
int dstBpp = remap ? ((int)_state.State.SetRemapComponentsNumDstComponents + 1) * componentSize : 1;
|
int dstComponents = (int)_state.State.SetRemapComponentsNumDstComponents + 1;
|
||||||
|
int srcBpp = remap ? srcComponents * componentSize : 1;
|
||||||
|
int dstBpp = remap ? dstComponents * componentSize : 1;
|
||||||
|
|
||||||
var dst = Unsafe.As<uint, DmaTexture>(ref _state.State.SetDstBlockSize);
|
var dst = Unsafe.As<uint, DmaTexture>(ref _state.State.SetDstBlockSize);
|
||||||
var src = Unsafe.As<uint, DmaTexture>(ref _state.State.SetSrcBlockSize);
|
var src = Unsafe.As<uint, DmaTexture>(ref _state.State.SetSrcBlockSize);
|
||||||
@@ -274,63 +340,51 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe bool Convert<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan) where T : unmanaged
|
|
||||||
{
|
|
||||||
if (srcLinear && dstLinear && srcBpp == dstBpp)
|
|
||||||
{
|
|
||||||
// Optimized path for purely linear copies - we don't need to calculate every single byte offset,
|
|
||||||
// and we can make use of Span.CopyTo which is very very fast (even compared to pointers)
|
|
||||||
for (int y = 0; y < yCount; y++)
|
|
||||||
{
|
|
||||||
srcCalculator.SetY(srcRegionY + y);
|
|
||||||
dstCalculator.SetY(dstRegionY + y);
|
|
||||||
int srcOffset = srcCalculator.GetOffset(srcRegionX);
|
|
||||||
int dstOffset = dstCalculator.GetOffset(dstRegionX);
|
|
||||||
srcSpan.Slice(srcOffset - srcBaseOffset, xCount * srcBpp)
|
|
||||||
.CopyTo(dstSpan.Slice(dstOffset - dstBaseOffset, xCount * dstBpp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan)
|
|
||||||
{
|
|
||||||
byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
|
|
||||||
byte* srcBase = srcPtr - srcBaseOffset;
|
|
||||||
|
|
||||||
for (int y = 0; y < yCount; y++)
|
|
||||||
{
|
|
||||||
srcCalculator.SetY(srcRegionY + y);
|
|
||||||
dstCalculator.SetY(dstRegionY + y);
|
|
||||||
|
|
||||||
for (int x = 0; x < xCount; x++)
|
|
||||||
{
|
|
||||||
int srcOffset = srcCalculator.GetOffset(srcRegionX + x);
|
|
||||||
int dstOffset = dstCalculator.GetOffset(dstRegionX + x);
|
|
||||||
|
|
||||||
*(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OPT: This allocates a (potentially) huge temporary array and then copies an existing
|
// OPT: This allocates a (potentially) huge temporary array and then copies an existing
|
||||||
// region of memory into it, data that might get overwritten entirely anyways. Ideally this should
|
// region of memory into it, data that might get overwritten entirely anyways. Ideally this should
|
||||||
// all be rewritten to use pooled arrays, but that gets complicated with packed data and strides
|
// all be rewritten to use pooled arrays, but that gets complicated with packed data and strides
|
||||||
Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray();
|
Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray();
|
||||||
|
|
||||||
bool _ = srcBpp switch
|
TextureParams srcParams = new TextureParams(srcRegionX, srcRegionY, srcBaseOffset, srcBpp, srcLinear, srcCalculator);
|
||||||
|
TextureParams dstParams = new TextureParams(dstRegionX, dstRegionY, dstBaseOffset, dstBpp, dstLinear, dstCalculator);
|
||||||
|
|
||||||
|
// If remapping is enabled, we always copy the components directly, in order.
|
||||||
|
// If it's enabled, but the mapping is just XYZW, we also copy them in order.
|
||||||
|
bool isIdentityRemap = !remap ||
|
||||||
|
(_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.SrcX &&
|
||||||
|
(dstComponents < 2 || _state.State.SetRemapComponentsDstY == SetRemapComponentsDst.SrcY) &&
|
||||||
|
(dstComponents < 3 || _state.State.SetRemapComponentsDstZ == SetRemapComponentsDst.SrcZ) &&
|
||||||
|
(dstComponents < 4 || _state.State.SetRemapComponentsDstW == SetRemapComponentsDst.SrcW));
|
||||||
|
|
||||||
|
if (isIdentityRemap)
|
||||||
{
|
{
|
||||||
1 => Convert<byte>(dstSpan, srcSpan),
|
// The order of the components doesn't change, so we can just copy directly
|
||||||
2 => Convert<ushort>(dstSpan, srcSpan),
|
// (with layout conversion if necessary).
|
||||||
4 => Convert<uint>(dstSpan, srcSpan),
|
|
||||||
8 => Convert<ulong>(dstSpan, srcSpan),
|
switch (srcBpp)
|
||||||
12 => Convert<Bpp12Pixel>(dstSpan, srcSpan),
|
{
|
||||||
16 => Convert<Vector128<byte>>(dstSpan, srcSpan),
|
case 1: Copy<byte>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
_ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
|
case 2: Copy<ushort>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
};
|
case 4: Copy<uint>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
case 8: Copy<ulong>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
case 12: Copy<Bpp12Pixel>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
case 16: Copy<Vector128<byte>>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
default: throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The order or value of the components might change.
|
||||||
|
|
||||||
|
switch (componentSize)
|
||||||
|
{
|
||||||
|
case 1: CopyShuffle<byte>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
case 2: CopyShuffle<ushort>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
case 3: CopyShuffle<UInt24>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
case 4: CopyShuffle<uint>(dstSpan, srcSpan, dstParams, srcParams); break;
|
||||||
|
default: throw new NotSupportedException($"Unable to copy ${componentSize} component size.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan);
|
memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan);
|
||||||
}
|
}
|
||||||
@@ -372,6 +426,133 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies data from one texture to another, while performing layout conversion if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Pixel type</typeparam>
|
||||||
|
/// <param name="dstSpan">Destination texture memory region</param>
|
||||||
|
/// <param name="srcSpan">Source texture memory region</param>
|
||||||
|
/// <param name="dst">Destination texture parameters</param>
|
||||||
|
/// <param name="src">Source texture parameters</param>
|
||||||
|
private unsafe void Copy<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan, TextureParams dst, TextureParams src) where T : unmanaged
|
||||||
|
{
|
||||||
|
int xCount = (int)_state.State.LineLengthIn;
|
||||||
|
int yCount = (int)_state.State.LineCount;
|
||||||
|
|
||||||
|
if (src.Linear && dst.Linear && src.Bpp == dst.Bpp)
|
||||||
|
{
|
||||||
|
// Optimized path for purely linear copies - we don't need to calculate every single byte offset,
|
||||||
|
// and we can make use of Span.CopyTo which is very very fast (even compared to pointers)
|
||||||
|
for (int y = 0; y < yCount; y++)
|
||||||
|
{
|
||||||
|
src.Calculator.SetY(src.RegionY + y);
|
||||||
|
dst.Calculator.SetY(dst.RegionY + y);
|
||||||
|
int srcOffset = src.Calculator.GetOffset(src.RegionX);
|
||||||
|
int dstOffset = dst.Calculator.GetOffset(dst.RegionX);
|
||||||
|
srcSpan.Slice(srcOffset - src.BaseOffset, xCount * src.Bpp)
|
||||||
|
.CopyTo(dstSpan.Slice(dstOffset - dst.BaseOffset, xCount * dst.Bpp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan)
|
||||||
|
{
|
||||||
|
byte* dstBase = dstPtr - dst.BaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
|
||||||
|
byte* srcBase = srcPtr - src.BaseOffset;
|
||||||
|
|
||||||
|
for (int y = 0; y < yCount; y++)
|
||||||
|
{
|
||||||
|
src.Calculator.SetY(src.RegionY + y);
|
||||||
|
dst.Calculator.SetY(dst.RegionY + y);
|
||||||
|
|
||||||
|
for (int x = 0; x < xCount; x++)
|
||||||
|
{
|
||||||
|
int srcOffset = src.Calculator.GetOffset(src.RegionX + x);
|
||||||
|
int dstOffset = dst.Calculator.GetOffset(dst.RegionX + x);
|
||||||
|
|
||||||
|
*(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets texture pixel data to a constant value, while performing layout conversion if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Pixel type</typeparam>
|
||||||
|
/// <param name="dstSpan">Destination texture memory region</param>
|
||||||
|
/// <param name="dst">Destination texture parameters</param>
|
||||||
|
/// <param name="fillValue">Constant pixel value to be set</param>
|
||||||
|
private unsafe void Fill<T>(Span<byte> dstSpan, TextureParams dst, T fillValue) where T : unmanaged
|
||||||
|
{
|
||||||
|
int xCount = (int)_state.State.LineLengthIn;
|
||||||
|
int yCount = (int)_state.State.LineCount;
|
||||||
|
|
||||||
|
fixed (byte* dstPtr = dstSpan)
|
||||||
|
{
|
||||||
|
byte* dstBase = dstPtr - dst.BaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
|
||||||
|
|
||||||
|
for (int y = 0; y < yCount; y++)
|
||||||
|
{
|
||||||
|
dst.Calculator.SetY(dst.RegionY + y);
|
||||||
|
|
||||||
|
for (int x = 0; x < xCount; x++)
|
||||||
|
{
|
||||||
|
int dstOffset = dst.Calculator.GetOffset(dst.RegionX + x);
|
||||||
|
|
||||||
|
*(T*)(dstBase + dstOffset) = fillValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies data from one texture to another, while performing layout conversion and component shuffling if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Pixel type</typeparam>
|
||||||
|
/// <param name="dstSpan">Destination texture memory region</param>
|
||||||
|
/// <param name="srcSpan">Source texture memory region</param>
|
||||||
|
/// <param name="dst">Destination texture parameters</param>
|
||||||
|
/// <param name="src">Source texture parameters</param>
|
||||||
|
private void CopyShuffle<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan, TextureParams dst, TextureParams src) where T : unmanaged
|
||||||
|
{
|
||||||
|
int dstComponents = (int)_state.State.SetRemapComponentsNumDstComponents + 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < dstComponents; i++)
|
||||||
|
{
|
||||||
|
SetRemapComponentsDst componentsDst = i switch
|
||||||
|
{
|
||||||
|
0 => _state.State.SetRemapComponentsDstX,
|
||||||
|
1 => _state.State.SetRemapComponentsDstY,
|
||||||
|
2 => _state.State.SetRemapComponentsDstZ,
|
||||||
|
_ => _state.State.SetRemapComponentsDstW
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (componentsDst)
|
||||||
|
{
|
||||||
|
case SetRemapComponentsDst.SrcX:
|
||||||
|
Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan, dst, src);
|
||||||
|
break;
|
||||||
|
case SetRemapComponentsDst.SrcY:
|
||||||
|
Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>()), dst, src);
|
||||||
|
break;
|
||||||
|
case SetRemapComponentsDst.SrcZ:
|
||||||
|
Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>() * 2), dst, src);
|
||||||
|
break;
|
||||||
|
case SetRemapComponentsDst.SrcW:
|
||||||
|
Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>() * 3), dst, src);
|
||||||
|
break;
|
||||||
|
case SetRemapComponentsDst.ConstA:
|
||||||
|
Fill<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstA));
|
||||||
|
break;
|
||||||
|
case SetRemapComponentsDst.ConstB:
|
||||||
|
Fill<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstB));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies block linear data with block linear GOBs to a block linear destination with linear GOBs.
|
/// Copies block linear data with block linear GOBs to a block linear destination with linear GOBs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -732,12 +732,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
break;
|
break;
|
||||||
case Target.Texture2DMultisample:
|
case Target.Texture2DMultisample:
|
||||||
case Target.Texture2DMultisampleArray:
|
case Target.Texture2DMultisampleArray:
|
||||||
// We don't support copy between multisample and non-multisample depth-stencil textures
|
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
|
||||||
// because there's no way to emulate that since most GPUs don't support writing a
|
|
||||||
// custom stencil value into the texture, among several other API limitations.
|
|
||||||
|
|
||||||
if ((rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) &&
|
|
||||||
!rhs.FormatInfo.Format.IsDepthOrStencil())
|
|
||||||
{
|
{
|
||||||
return TextureViewCompatibility.CopyOnly;
|
return TextureViewCompatibility.CopyOnly;
|
||||||
}
|
}
|
||||||
|
@@ -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 = 4404;
|
private const uint CodeGenVersion = 4735;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -112,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision;
|
||||||
|
|
||||||
public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
|
public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
|
||||||
|
|
||||||
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
|
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
|
||||||
@@ -142,7 +144,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
|
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
|
||||||
|
|
||||||
public bool QueryHostSupportsViewportIndex() => _context.Capabilities.SupportsViewportIndex;
|
public bool QueryHostSupportsViewportIndexVertexTessellation() => _context.Capabilities.SupportsViewportIndexVertexTessellation;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a packed Maxwell texture format to the shader translator texture format.
|
/// Converts a packed Maxwell texture format to the shader translator texture format.
|
||||||
|
@@ -449,7 +449,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
if (translatorContexts[i] != null)
|
if (translatorContexts[i] != null)
|
||||||
{
|
{
|
||||||
translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute);
|
translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute);
|
||||||
translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null);
|
translatorContexts[i].SetLastInVertexPipeline();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
|
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
|
||||||
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
|
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
|
||||||
private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array"));
|
private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array"));
|
||||||
|
private static readonly Lazy<bool> _supportsViewportArray2 = new Lazy<bool>(() => HasExtension("GL_NV_viewport_array2"));
|
||||||
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
|
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
|
||||||
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
|
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
|
||||||
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
|
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
|
||||||
@@ -65,6 +66,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
|
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
|
||||||
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
|
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
|
||||||
public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value;
|
public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value;
|
||||||
|
public static bool SupportsViewportArray2 => _supportsViewportArray2.Value;
|
||||||
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
|
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
|
||||||
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
|
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
|
||||||
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
|
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
|
||||||
|
103
Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
Normal file
103
Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
{
|
||||||
|
class IntermediatePool : IDisposable
|
||||||
|
{
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private readonly List<TextureView> _entries;
|
||||||
|
|
||||||
|
public IntermediatePool(OpenGLRenderer renderer)
|
||||||
|
{
|
||||||
|
_renderer = renderer;
|
||||||
|
_entries = new List<TextureView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureView GetOrCreateWithAtLeast(
|
||||||
|
Target target,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int bytesPerPixel,
|
||||||
|
Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depth,
|
||||||
|
int levels,
|
||||||
|
int samples)
|
||||||
|
{
|
||||||
|
TextureView entry;
|
||||||
|
|
||||||
|
for (int i = 0; i < _entries.Count; i++)
|
||||||
|
{
|
||||||
|
entry = _entries[i];
|
||||||
|
|
||||||
|
if (entry.Target == target && entry.Format == format && entry.Info.Samples == samples)
|
||||||
|
{
|
||||||
|
if (entry.Width < width ||
|
||||||
|
entry.Height < height ||
|
||||||
|
entry.Info.Depth < depth ||
|
||||||
|
entry.Info.Levels < levels)
|
||||||
|
{
|
||||||
|
width = Math.Max(width, entry.Width);
|
||||||
|
height = Math.Max(height, entry.Height);
|
||||||
|
depth = Math.Max(depth, entry.Info.Depth);
|
||||||
|
levels = Math.Max(levels, entry.Info.Levels);
|
||||||
|
|
||||||
|
entry.Dispose();
|
||||||
|
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels, samples);
|
||||||
|
_entries[i] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels, samples);
|
||||||
|
_entries.Add(entry);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextureView CreateNew(
|
||||||
|
Target target,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int bytesPerPixel,
|
||||||
|
Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depth,
|
||||||
|
int levels,
|
||||||
|
int samples)
|
||||||
|
{
|
||||||
|
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
levels,
|
||||||
|
samples,
|
||||||
|
blockWidth,
|
||||||
|
blockHeight,
|
||||||
|
bytesPerPixel,
|
||||||
|
format,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
target,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha), 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (TextureView entry in _entries)
|
||||||
|
{
|
||||||
|
entry.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_entries.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -15,9 +15,12 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
private int _copyPboHandle;
|
private int _copyPboHandle;
|
||||||
private int _copyPboSize;
|
private int _copyPboSize;
|
||||||
|
|
||||||
|
public IntermediatePool IntermediatePool { get; }
|
||||||
|
|
||||||
public TextureCopy(OpenGLRenderer renderer)
|
public TextureCopy(OpenGLRenderer renderer)
|
||||||
{
|
{
|
||||||
_renderer = renderer;
|
_renderer = renderer;
|
||||||
|
IntermediatePool = new IntermediatePool(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Copy(
|
public void Copy(
|
||||||
@@ -514,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
_copyPboHandle = 0;
|
_copyPboHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IntermediatePool.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -117,12 +117,20 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
{
|
{
|
||||||
TextureView destinationView = (TextureView)destination;
|
TextureView destinationView = (TextureView)destination;
|
||||||
|
|
||||||
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
bool srcIsMultisample = Target.IsMultisample();
|
||||||
|
bool dstIsMultisample = destinationView.Target.IsMultisample();
|
||||||
|
|
||||||
|
if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil())
|
||||||
|
{
|
||||||
|
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||||
|
CopyWithBlitForDepthMS(destinationView, 0, firstLayer, layers);
|
||||||
|
}
|
||||||
|
else if (!dstIsMultisample && srcIsMultisample)
|
||||||
{
|
{
|
||||||
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||||
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
|
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
|
||||||
}
|
}
|
||||||
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
else if (dstIsMultisample && !srcIsMultisample)
|
||||||
{
|
{
|
||||||
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||||
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
|
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
|
||||||
@@ -143,11 +151,18 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
{
|
{
|
||||||
TextureView destinationView = (TextureView)destination;
|
TextureView destinationView = (TextureView)destination;
|
||||||
|
|
||||||
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
bool srcIsMultisample = Target.IsMultisample();
|
||||||
|
bool dstIsMultisample = destinationView.Target.IsMultisample();
|
||||||
|
|
||||||
|
if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil())
|
||||||
|
{
|
||||||
|
CopyWithBlitForDepthMS(destinationView, srcLayer, dstLayer, 1);
|
||||||
|
}
|
||||||
|
else if (!dstIsMultisample && srcIsMultisample)
|
||||||
{
|
{
|
||||||
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1);
|
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1);
|
||||||
}
|
}
|
||||||
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
else if (dstIsMultisample && !srcIsMultisample)
|
||||||
{
|
{
|
||||||
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
|
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
|
||||||
}
|
}
|
||||||
@@ -161,6 +176,61 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CopyWithBlitForDepthMS(TextureView destinationView, int srcLayer, int dstLayer, int layers)
|
||||||
|
{
|
||||||
|
// This is currently used for multisample <-> non-multisample copies.
|
||||||
|
// We can't do that with compute because it's not possible to write depth textures on compute.
|
||||||
|
// It can be done with draws, but we don't have support for saving and restoring the OpenGL state
|
||||||
|
// for a draw with different state right now.
|
||||||
|
// This approach uses blit, which causes a resolution loss since some samples will be lost
|
||||||
|
// in the process.
|
||||||
|
|
||||||
|
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
||||||
|
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
||||||
|
|
||||||
|
if (destinationView.Target.IsMultisample())
|
||||||
|
{
|
||||||
|
TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast(
|
||||||
|
Info.Target,
|
||||||
|
Info.BlockWidth,
|
||||||
|
Info.BlockHeight,
|
||||||
|
Info.BytesPerPixel,
|
||||||
|
Format,
|
||||||
|
destinationView.Width,
|
||||||
|
destinationView.Height,
|
||||||
|
Info.Depth,
|
||||||
|
1,
|
||||||
|
1);
|
||||||
|
|
||||||
|
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, dstRegion, false);
|
||||||
|
_renderer.TextureCopy.Copy(intermmediate, destinationView, dstRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Target target = Target switch
|
||||||
|
{
|
||||||
|
Target.Texture2DMultisample => Target.Texture2D,
|
||||||
|
Target.Texture2DMultisampleArray => Target.Texture2DArray,
|
||||||
|
_ => Target
|
||||||
|
};
|
||||||
|
|
||||||
|
TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast(
|
||||||
|
target,
|
||||||
|
Info.BlockWidth,
|
||||||
|
Info.BlockHeight,
|
||||||
|
Info.BytesPerPixel,
|
||||||
|
Format,
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
Info.Depth,
|
||||||
|
1,
|
||||||
|
1);
|
||||||
|
|
||||||
|
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, false);
|
||||||
|
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
||||||
{
|
{
|
||||||
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
||||||
|
@@ -103,11 +103,14 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
public Capabilities GetCapabilities()
|
public Capabilities GetCapabilities()
|
||||||
{
|
{
|
||||||
|
bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows;
|
||||||
|
bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows;
|
||||||
|
|
||||||
return new Capabilities(
|
return new Capabilities(
|
||||||
api: TargetApi.OpenGL,
|
api: TargetApi.OpenGL,
|
||||||
vendorName: GpuVendor,
|
vendorName: GpuVendor,
|
||||||
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
|
hasFrontFacingBug: intelWindows,
|
||||||
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
|
hasVectorIndexingBug: amdWindows,
|
||||||
needsFragmentOutputSpecialization: false,
|
needsFragmentOutputSpecialization: false,
|
||||||
reduceShaderPrecision: false,
|
reduceShaderPrecision: false,
|
||||||
supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
|
supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
|
||||||
@@ -133,7 +136,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||||
supportsViewportIndex: HwCapabilities.SupportsShaderViewportLayerArray,
|
supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||||
|
supportsViewportMask: HwCapabilities.SupportsViewportArray2,
|
||||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||||
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
||||||
@@ -142,7 +146,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
maximumImagesPerStage: 8,
|
maximumImagesPerStage: 8,
|
||||||
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
|
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
|
||||||
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
|
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
|
||||||
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment);
|
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
||||||
|
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||||
|
@@ -226,6 +226,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
|
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
|
||||||
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
|
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
|
||||||
GL.Viewport(0, 0, _width, _height);
|
GL.Viewport(0, 0, _width, _height);
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer);
|
||||||
|
|
||||||
swapBuffersCallback();
|
swapBuffersCallback();
|
||||||
|
|
||||||
|
@@ -59,6 +59,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable");
|
context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.Config.GpuAccessor.QueryHostSupportsViewportMask())
|
||||||
|
{
|
||||||
|
context.AppendLine("#extension GL_NV_viewport_array2 : enable");
|
||||||
|
}
|
||||||
|
|
||||||
context.AppendLine("#pragma optionNV(fastmath off)");
|
context.AppendLine("#pragma optionNV(fastmath off)");
|
||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
|
|
||||||
@@ -215,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
||||||
{
|
{
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX);
|
var tfOutput = context.Config.GetTransformFeedbackOutput(AttributeConsts.PositionX);
|
||||||
if (tfOutput.Valid)
|
if (tfOutput.Valid)
|
||||||
{
|
{
|
||||||
context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex");
|
context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex");
|
||||||
@@ -552,7 +557,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr)
|
private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr)
|
||||||
{
|
{
|
||||||
string suffix = AttributeInfo.IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty;
|
string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty;
|
||||||
string iq = string.Empty;
|
string iq = string.Empty;
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
if (context.Config.Stage == ShaderStage.Fragment)
|
||||||
@@ -569,7 +574,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment)
|
if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
for (int c = 0; c < 4; c++)
|
int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0);
|
||||||
|
|
||||||
|
if (components > 1)
|
||||||
|
{
|
||||||
|
string type = components switch
|
||||||
|
{
|
||||||
|
2 => "vec2",
|
||||||
|
3 => "vec3",
|
||||||
|
4 => "vec4",
|
||||||
|
_ => "float"
|
||||||
|
};
|
||||||
|
|
||||||
|
context.AppendLine($"layout (location = {attr}) in {type} {name};");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = components > 1 ? components : 0; c < 4; c++)
|
||||||
{
|
{
|
||||||
char swzMask = "xyzw"[c];
|
char swzMask = "xyzw"[c];
|
||||||
|
|
||||||
@@ -636,13 +656,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr)
|
private static void DeclareOutputAttribute(CodeGenContext context, int attr)
|
||||||
{
|
{
|
||||||
string suffix = AttributeInfo.IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty;
|
string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty;
|
||||||
string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}";
|
string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}";
|
||||||
|
|
||||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
||||||
{
|
{
|
||||||
int attrOffset = AttributeConsts.UserAttributeBase + attr * 16;
|
int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0);
|
||||||
int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
|
|
||||||
|
|
||||||
if (components > 1)
|
if (components > 1)
|
||||||
{
|
{
|
||||||
@@ -656,7 +675,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
string xfb = string.Empty;
|
string xfb = string.Empty;
|
||||||
|
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset);
|
var tfOutput = context.Config.GetTransformFeedbackOutput(attr, 0);
|
||||||
if (tfOutput.Valid)
|
if (tfOutput.Valid)
|
||||||
{
|
{
|
||||||
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
|
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
|
||||||
@@ -664,15 +683,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};");
|
context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
for (int c = components > 1 ? components : 0; c < 4; c++)
|
||||||
for (int c = 0; c < 4; c++)
|
|
||||||
{
|
{
|
||||||
char swzMask = "xyzw"[c];
|
char swzMask = "xyzw"[c];
|
||||||
|
|
||||||
string xfb = string.Empty;
|
string xfb = string.Empty;
|
||||||
|
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4);
|
var tfOutput = context.Config.GetTransformFeedbackOutput(attr, c);
|
||||||
if (tfOutput.Valid)
|
if (tfOutput.Valid)
|
||||||
{
|
{
|
||||||
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
|
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
|
||||||
@@ -681,7 +699,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
|
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string type = context.Config.Stage != ShaderStage.Fragment ? "vec4" :
|
string type = context.Config.Stage != ShaderStage.Fragment ? "vec4" :
|
||||||
@@ -712,6 +729,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
|
context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr)
|
||||||
|
{
|
||||||
|
if (isOutAttr)
|
||||||
|
{
|
||||||
|
return stage == ShaderStage.TessellationControl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return stage == ShaderStage.TessellationControl ||
|
||||||
|
stage == ShaderStage.TessellationEvaluation ||
|
||||||
|
stage == ShaderStage.Geometry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
||||||
{
|
{
|
||||||
foreach (int attr in attrs.Order())
|
foreach (int attr in attrs.Order())
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
|
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
@@ -126,21 +125,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
}
|
}
|
||||||
else if (node is AstAssignment assignment)
|
else if (node is AstAssignment assignment)
|
||||||
{
|
{
|
||||||
|
AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination);
|
||||||
AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
|
AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
|
||||||
AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true);
|
|
||||||
|
|
||||||
string dest;
|
|
||||||
|
|
||||||
if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute())
|
|
||||||
{
|
|
||||||
bool perPatch = operand.Type == OperandType.AttributePerPatch;
|
|
||||||
dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dest = InstGen.GetExpression(context, assignment.Destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
string dest = InstGen.GetExpression(context, assignment.Destination);
|
||||||
string src = ReinterpretCast(context, assignment.Source, srcType, dstType);
|
string src = ReinterpretCast(context, assignment.Source, srcType, dstType);
|
||||||
|
|
||||||
context.AppendLine(dest + " = " + src + ";");
|
context.AppendLine(dest + " = " + src + ";");
|
||||||
|
@@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
// For shared memory access, the second argument is unused and should be ignored.
|
// For shared memory access, the second argument is unused and should be ignored.
|
||||||
// It is there to make both storage and shared access have the same number of arguments.
|
// It is there to make both storage and shared access have the same number of arguments.
|
||||||
// For storage, both inputs are consumed when the argument index is 0, so we should skip it here.
|
// For storage, both inputs are consumed when the argument index is 0, so we should skip it here.
|
||||||
if (argIndex == 1 && (atomic || (inst & Instruction.MrMask) == Instruction.MrShared))
|
if (argIndex == 1 && (atomic || operation.StorageKind == StorageKind.SharedMemory))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -85,14 +85,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
if (argIndex == 0 && atomic)
|
if (argIndex == 0 && atomic)
|
||||||
{
|
{
|
||||||
Instruction memRegion = inst & Instruction.MrMask;
|
switch (operation.StorageKind)
|
||||||
|
|
||||||
switch (memRegion)
|
|
||||||
{
|
{
|
||||||
case Instruction.MrShared: args += LoadShared(context, operation); break;
|
case StorageKind.SharedMemory: args += LoadShared(context, operation); break;
|
||||||
case Instruction.MrStorage: args += LoadStorage(context, operation); break;
|
case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break;
|
||||||
|
|
||||||
default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\".");
|
default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -166,8 +164,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.ImageAtomic:
|
case Instruction.ImageAtomic:
|
||||||
return ImageLoadOrStore(context, operation);
|
return ImageLoadOrStore(context, operation);
|
||||||
|
|
||||||
case Instruction.LoadAttribute:
|
case Instruction.Load:
|
||||||
return LoadAttribute(context, operation);
|
return Load(context, operation);
|
||||||
|
|
||||||
case Instruction.LoadConstant:
|
case Instruction.LoadConstant:
|
||||||
return LoadConstant(context, operation);
|
return LoadConstant(context, operation);
|
||||||
@@ -193,8 +191,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.PackHalf2x16:
|
case Instruction.PackHalf2x16:
|
||||||
return PackHalf2x16(context, operation);
|
return PackHalf2x16(context, operation);
|
||||||
|
|
||||||
case Instruction.StoreAttribute:
|
case Instruction.Store:
|
||||||
return StoreAttribute(context, operation);
|
return Store(context, operation);
|
||||||
|
|
||||||
case Instruction.StoreLocal:
|
case Instruction.StoreLocal:
|
||||||
return StoreLocal(context, operation);
|
return StoreLocal(context, operation);
|
||||||
|
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.ImageStore, InstType.Special);
|
Add(Instruction.ImageStore, InstType.Special);
|
||||||
Add(Instruction.ImageAtomic, InstType.Special);
|
Add(Instruction.ImageAtomic, InstType.Special);
|
||||||
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
|
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
|
||||||
Add(Instruction.LoadAttribute, InstType.Special);
|
Add(Instruction.Load, InstType.Special);
|
||||||
Add(Instruction.LoadConstant, InstType.Special);
|
Add(Instruction.LoadConstant, InstType.Special);
|
||||||
Add(Instruction.LoadLocal, InstType.Special);
|
Add(Instruction.LoadLocal, InstType.Special);
|
||||||
Add(Instruction.LoadShared, InstType.Special);
|
Add(Instruction.LoadShared, InstType.Special);
|
||||||
@@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor);
|
Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor);
|
||||||
Add(Instruction.Sine, InstType.CallUnary, "sin");
|
Add(Instruction.Sine, InstType.CallUnary, "sin");
|
||||||
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
||||||
Add(Instruction.StoreAttribute, InstType.Special);
|
Add(Instruction.Store, InstType.Special);
|
||||||
Add(Instruction.StoreLocal, InstType.Special);
|
Add(Instruction.StoreLocal, InstType.Special);
|
||||||
Add(Instruction.StoreShared, InstType.Special);
|
Add(Instruction.StoreShared, InstType.Special);
|
||||||
Add(Instruction.StoreShared16, InstType.Special);
|
Add(Instruction.StoreShared16, InstType.Special);
|
||||||
|
@@ -210,30 +210,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return texCallBuilder.ToString();
|
return texCallBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LoadAttribute(CodeGenContext context, AstOperation operation)
|
public static string Load(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
IAstNode src1 = operation.GetSource(0);
|
return GenerateLoadOrStore(context, operation, isStore: false);
|
||||||
IAstNode src2 = operation.GetSource(1);
|
|
||||||
IAstNode src3 = operation.GetSource(2);
|
|
||||||
|
|
||||||
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand.");
|
|
||||||
}
|
|
||||||
|
|
||||||
string indexExpr = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2));
|
|
||||||
|
|
||||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
|
||||||
{
|
|
||||||
int attrOffset = baseAttr.Value + (operand.Value << 2);
|
|
||||||
return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
|
||||||
attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
|
||||||
return OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: false, indexExpr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LoadConstant(CodeGenContext context, AstOperation operation)
|
public static string LoadConstant(CodeGenContext context, AstOperation operation)
|
||||||
@@ -337,33 +316,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
|
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string StoreAttribute(CodeGenContext context, AstOperation operation)
|
public static string Store(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
IAstNode src1 = operation.GetSource(0);
|
return GenerateLoadOrStore(context, operation, isStore: true);
|
||||||
IAstNode src2 = operation.GetSource(1);
|
|
||||||
IAstNode src3 = operation.GetSource(2);
|
|
||||||
|
|
||||||
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"First input of {nameof(Instruction.StoreAttribute)} must be a constant operand.");
|
|
||||||
}
|
|
||||||
|
|
||||||
string attrName;
|
|
||||||
|
|
||||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
|
||||||
{
|
|
||||||
int attrOffset = baseAttr.Value + (operand.Value << 2);
|
|
||||||
attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
|
||||||
attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
|
||||||
attrName = OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
string value = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2));
|
|
||||||
return $"{attrName} = {value}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string StoreLocal(CodeGenContext context, AstOperation operation)
|
public static string StoreLocal(CodeGenContext context, AstOperation operation)
|
||||||
@@ -677,7 +632,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
Append(ApplyScaling(AssemblePVector(pCount)));
|
string ApplyBias(string vector)
|
||||||
|
{
|
||||||
|
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
|
||||||
|
if (isGather && gatherBiasPrecision != 0)
|
||||||
|
{
|
||||||
|
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
|
||||||
|
// Offset by the gather precision divided by 2 to correct for rounding.
|
||||||
|
|
||||||
|
if (pCount == 1)
|
||||||
|
{
|
||||||
|
vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
Append(ApplyBias(ApplyScaling(AssemblePVector(pCount))));
|
||||||
|
|
||||||
string AssembleDerivativesVector(int count)
|
string AssembleDerivativesVector(int count)
|
||||||
{
|
{
|
||||||
@@ -826,6 +802,111 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
||||||
|
{
|
||||||
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
|
string varName;
|
||||||
|
AggregateType varType;
|
||||||
|
int srcIndex = 0;
|
||||||
|
|
||||||
|
switch (storageKind)
|
||||||
|
{
|
||||||
|
case StorageKind.Input:
|
||||||
|
case StorageKind.InputPerPatch:
|
||||||
|
case StorageKind.Output:
|
||||||
|
case StorageKind.OutputPerPatch:
|
||||||
|
if (!(operation.GetSource(srcIndex++) is AstOperand varId) || varId.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IoVariable ioVariable = (IoVariable)varId.Value;
|
||||||
|
bool isOutput = storageKind.IsOutput();
|
||||||
|
bool isPerPatch = storageKind.IsPerPatch();
|
||||||
|
int location = -1;
|
||||||
|
int component = 0;
|
||||||
|
|
||||||
|
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||||
|
{
|
||||||
|
if (!(operation.GetSource(srcIndex++) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
location = vecIndex.Value;
|
||||||
|
|
||||||
|
if (operation.SourcesCount > srcIndex &&
|
||||||
|
operation.GetSource(srcIndex) is AstOperand elemIndex &&
|
||||||
|
elemIndex.Type == OperandType.Constant &&
|
||||||
|
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput))
|
||||||
|
{
|
||||||
|
component = elemIndex.Value;
|
||||||
|
srcIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(varName, varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch);
|
||||||
|
|
||||||
|
if (IoMap.IsPerVertexBuiltIn(context.Config.Stage, ioVariable, isOutput))
|
||||||
|
{
|
||||||
|
// Since those exist both as input and output on geometry and tessellation shaders,
|
||||||
|
// we need the gl_in and gl_out prefixes to disambiguate.
|
||||||
|
|
||||||
|
if (storageKind == StorageKind.Input)
|
||||||
|
{
|
||||||
|
string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32);
|
||||||
|
varName = $"gl_in[{expr}].{varName}";
|
||||||
|
}
|
||||||
|
else if (storageKind == StorageKind.Output)
|
||||||
|
{
|
||||||
|
string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32);
|
||||||
|
varName = $"gl_out[{expr}].{varName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstSrcIndex = srcIndex;
|
||||||
|
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
|
||||||
|
|
||||||
|
for (; srcIndex < inputsCount; srcIndex++)
|
||||||
|
{
|
||||||
|
IAstNode src = operation.GetSource(srcIndex);
|
||||||
|
|
||||||
|
if ((varType & AggregateType.ElementCountMask) != 0 &&
|
||||||
|
srcIndex == inputsCount - 1 &&
|
||||||
|
src is AstOperand elementIndex &&
|
||||||
|
elementIndex.Type == OperandType.Constant)
|
||||||
|
{
|
||||||
|
varName += "." + "xyzw"[elementIndex.Value & 3];
|
||||||
|
}
|
||||||
|
else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output)
|
||||||
|
{
|
||||||
|
// GLSL requires that for tessellation control shader outputs,
|
||||||
|
// that the index expression must be *exactly* "gl_InvocationID",
|
||||||
|
// otherwise the compilation fails.
|
||||||
|
// TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR.
|
||||||
|
varName += "[gl_InvocationID]";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStore)
|
||||||
|
{
|
||||||
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
varName = $"{varName} = {GetSoureExpr(context, operation.GetSource(srcIndex), varType)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return varName;
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
|
private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
|
||||||
{
|
{
|
||||||
string sbName = OperandManager.GetShaderStagePrefix(stage);
|
string sbName = OperandManager.GetShaderStagePrefix(stage);
|
||||||
|
145
Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs
Normal file
145
Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
|
{
|
||||||
|
static class IoMap
|
||||||
|
{
|
||||||
|
public static (string, AggregateType) GetGlslVariable(
|
||||||
|
ShaderConfig config,
|
||||||
|
IoVariable ioVariable,
|
||||||
|
int location,
|
||||||
|
int component,
|
||||||
|
bool isOutput,
|
||||||
|
bool isPerPatch)
|
||||||
|
{
|
||||||
|
return ioVariable switch
|
||||||
|
{
|
||||||
|
IoVariable.BackColorDiffuse => ("gl_BackColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
|
IoVariable.BackColorSpecular => ("gl_BackSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
|
IoVariable.BaseInstance => ("gl_BaseInstanceARB", AggregateType.S32),
|
||||||
|
IoVariable.BaseVertex => ("gl_BaseVertexARB", AggregateType.S32),
|
||||||
|
IoVariable.ClipDistance => ("gl_ClipDistance", AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.CtaId => ("gl_WorkGroupID", AggregateType.Vector3 | AggregateType.U32),
|
||||||
|
IoVariable.DrawIndex => ("gl_DrawIDARB", AggregateType.S32),
|
||||||
|
IoVariable.FogCoord => ("gl_FogFragCoord", AggregateType.FP32), // Deprecated.
|
||||||
|
IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32),
|
||||||
|
IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location),
|
||||||
|
IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32),
|
||||||
|
IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool),
|
||||||
|
IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
|
IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
|
IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool),
|
||||||
|
IoVariable.InstanceId => ("gl_InstanceID", AggregateType.S32),
|
||||||
|
IoVariable.InstanceIndex => ("gl_InstanceIndex", AggregateType.S32),
|
||||||
|
IoVariable.InvocationId => ("gl_InvocationID", AggregateType.S32),
|
||||||
|
IoVariable.Layer => ("gl_Layer", AggregateType.S32),
|
||||||
|
IoVariable.PatchVertices => ("gl_PatchVerticesIn", AggregateType.S32),
|
||||||
|
IoVariable.PointCoord => ("gl_PointCoord", AggregateType.Vector2 | AggregateType.FP32),
|
||||||
|
IoVariable.PointSize => ("gl_PointSize", AggregateType.FP32),
|
||||||
|
IoVariable.Position => ("gl_Position", AggregateType.Vector4 | AggregateType.FP32),
|
||||||
|
IoVariable.PrimitiveId => GetPrimitiveIdVariableName(config.Stage, isOutput),
|
||||||
|
IoVariable.SubgroupEqMask => GetSubgroupMaskVariableName(config, "Eq"),
|
||||||
|
IoVariable.SubgroupGeMask => GetSubgroupMaskVariableName(config, "Ge"),
|
||||||
|
IoVariable.SubgroupGtMask => GetSubgroupMaskVariableName(config, "Gt"),
|
||||||
|
IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config),
|
||||||
|
IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"),
|
||||||
|
IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"),
|
||||||
|
IoVariable.SupportBlockRenderScale => (DefaultNames.SupportBlockRenderScaleName, AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.SupportBlockViewInverse => (DefaultNames.SupportBlockViewportInverse, AggregateType.Vector2 | AggregateType.FP32),
|
||||||
|
IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32),
|
||||||
|
IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.TextureCoord => ("gl_TexCoord", AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
|
IoVariable.ThreadId => ("gl_LocalInvocationID", AggregateType.Vector3 | AggregateType.U32),
|
||||||
|
IoVariable.ThreadKill => ("gl_HelperInvocation", AggregateType.Bool),
|
||||||
|
IoVariable.UserDefined => GetUserDefinedVariableName(config, location, component, isOutput, isPerPatch),
|
||||||
|
IoVariable.VertexId => ("gl_VertexID", AggregateType.S32),
|
||||||
|
IoVariable.VertexIndex => ("gl_VertexIndex", AggregateType.S32),
|
||||||
|
IoVariable.ViewportIndex => ("gl_ViewportIndex", AggregateType.S32),
|
||||||
|
IoVariable.ViewportMask => ("gl_ViewportMask", AggregateType.Array | AggregateType.S32),
|
||||||
|
_ => (null, AggregateType.Invalid)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPerVertexBuiltIn(ShaderStage stage, IoVariable ioVariable, bool isOutput)
|
||||||
|
{
|
||||||
|
switch (ioVariable)
|
||||||
|
{
|
||||||
|
case IoVariable.Layer:
|
||||||
|
case IoVariable.ViewportIndex:
|
||||||
|
case IoVariable.PointSize:
|
||||||
|
case IoVariable.Position:
|
||||||
|
case IoVariable.ClipDistance:
|
||||||
|
case IoVariable.PointCoord:
|
||||||
|
case IoVariable.ViewportMask:
|
||||||
|
if (isOutput)
|
||||||
|
{
|
||||||
|
return stage == ShaderStage.TessellationControl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return stage == ShaderStage.TessellationControl ||
|
||||||
|
stage == ShaderStage.TessellationEvaluation ||
|
||||||
|
stage == ShaderStage.Geometry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, AggregateType) GetFragmentOutputColorVariableName(ShaderConfig config, int location)
|
||||||
|
{
|
||||||
|
if (location < 0)
|
||||||
|
{
|
||||||
|
return (DefaultNames.OAttributePrefix, config.GetFragmentOutputColorType(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = DefaultNames.OAttributePrefix + location.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
return (name, config.GetFragmentOutputColorType(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, AggregateType) GetPrimitiveIdVariableName(ShaderStage stage, bool isOutput)
|
||||||
|
{
|
||||||
|
// The geometry stage has an additional gl_PrimitiveIDIn variable.
|
||||||
|
return (isOutput || stage != ShaderStage.Geometry ? "gl_PrimitiveID" : "gl_PrimitiveIDIn", AggregateType.S32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, AggregateType) GetSubgroupMaskVariableName(ShaderConfig config, string cc)
|
||||||
|
{
|
||||||
|
return config.GpuAccessor.QueryHostSupportsShaderBallot()
|
||||||
|
? ($"unpackUint2x32(gl_SubGroup{cc}MaskARB)", AggregateType.Vector2 | AggregateType.U32)
|
||||||
|
: ($"gl_Subgroup{cc}Mask", AggregateType.Vector4 | AggregateType.U32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, AggregateType) GetSubgroupInvocationIdVariableName(ShaderConfig config)
|
||||||
|
{
|
||||||
|
return config.GpuAccessor.QueryHostSupportsShaderBallot()
|
||||||
|
? ("gl_SubGroupInvocationARB", AggregateType.U32)
|
||||||
|
: ("gl_SubgroupInvocationID", AggregateType.U32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, AggregateType) GetUserDefinedVariableName(ShaderConfig config, int location, int component, bool isOutput, bool isPerPatch)
|
||||||
|
{
|
||||||
|
string name = isPerPatch
|
||||||
|
? DefaultNames.PerPatchAttributePrefix
|
||||||
|
: (isOutput ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix);
|
||||||
|
|
||||||
|
if (location < 0)
|
||||||
|
{
|
||||||
|
return (name, config.GetUserDefinedType(0, isOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
name += location.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
if (config.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput))
|
||||||
|
{
|
||||||
|
name += "_" + "xyzw"[component & 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (name, config.GetUserDefinedType(location, isOutput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +1,10 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||||
|
|
||||||
@@ -12,82 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
{
|
{
|
||||||
class OperandManager
|
class OperandManager
|
||||||
{
|
{
|
||||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||||
|
|
||||||
private readonly struct BuiltInAttribute
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public AggregateType Type { get; }
|
|
||||||
|
|
||||||
public BuiltInAttribute(string name, AggregateType type)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
|
|
||||||
{
|
|
||||||
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) },
|
|
||||||
|
|
||||||
// Special.
|
|
||||||
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
|
|
||||||
// Support uniforms.
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) },
|
|
||||||
|
|
||||||
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) }
|
|
||||||
};
|
|
||||||
|
|
||||||
private Dictionary<AstOperand, string> _locals;
|
private Dictionary<AstOperand, string> _locals;
|
||||||
|
|
||||||
@@ -110,8 +35,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
return operand.Type switch
|
return operand.Type switch
|
||||||
{
|
{
|
||||||
OperandType.Argument => GetArgumentName(operand.Value),
|
OperandType.Argument => GetArgumentName(operand.Value),
|
||||||
OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false),
|
|
||||||
OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true),
|
|
||||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
|
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
|
||||||
OperandType.LocalVariable => _locals[operand],
|
OperandType.LocalVariable => _locals[operand],
|
||||||
@@ -155,177 +78,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch)
|
|
||||||
{
|
|
||||||
return GetAttributeName(context, value, perPatch, isOutAttr: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
|
|
||||||
{
|
|
||||||
ShaderConfig config = context.Config;
|
|
||||||
|
|
||||||
if ((value & AttributeConsts.LoadOutputMask) != 0)
|
|
||||||
{
|
|
||||||
isOutAttr = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value &= AttributeConsts.Mask & ~3;
|
|
||||||
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
|
||||||
|
|
||||||
if (perPatch)
|
|
||||||
{
|
|
||||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
|
||||||
{
|
|
||||||
value -= AttributeConsts.UserAttributePerPatchBase;
|
|
||||||
|
|
||||||
return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}";
|
|
||||||
}
|
|
||||||
else if (value < AttributeConsts.UserAttributePerPatchBase)
|
|
||||||
{
|
|
||||||
return value switch
|
|
||||||
{
|
|
||||||
AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]",
|
|
||||||
AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]",
|
|
||||||
AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]",
|
|
||||||
AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]",
|
|
||||||
AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]",
|
|
||||||
AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]",
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
int attrOffset = value;
|
|
||||||
value -= AttributeConsts.UserAttributeBase;
|
|
||||||
|
|
||||||
string prefix = isOutAttr
|
|
||||||
? DefaultNames.OAttributePrefix
|
|
||||||
: DefaultNames.IAttributePrefix;
|
|
||||||
|
|
||||||
bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing);
|
|
||||||
|
|
||||||
if (indexable)
|
|
||||||
{
|
|
||||||
string name = prefix;
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
|
||||||
{
|
|
||||||
name += $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name + $"[{(value >> 4)}]." + swzMask;
|
|
||||||
}
|
|
||||||
else if (config.TransformFeedbackEnabled &&
|
|
||||||
((config.LastInVertexPipeline && isOutAttr) ||
|
|
||||||
(config.Stage == ShaderStage.Fragment && !isOutAttr)))
|
|
||||||
{
|
|
||||||
int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
|
|
||||||
string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}";
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
|
||||||
{
|
|
||||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return components > 1 ? name + '.' + swzMask : name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string name = $"{prefix}{(value >> 4)}";
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
|
||||||
{
|
|
||||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name + '.' + swzMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
|
|
||||||
{
|
|
||||||
value -= AttributeConsts.FragmentOutputColorBase;
|
|
||||||
|
|
||||||
return $"{DefaultNames.OAttributePrefix}{(value >> 4)}.{swzMask}";
|
|
||||||
}
|
|
||||||
else if (_builtInAttributes.TryGetValue(value, out BuiltInAttribute builtInAttr))
|
|
||||||
{
|
|
||||||
string subgroupMask = value switch
|
|
||||||
{
|
|
||||||
AttributeConsts.EqMask => "Eq",
|
|
||||||
AttributeConsts.GeMask => "Ge",
|
|
||||||
AttributeConsts.GtMask => "Gt",
|
|
||||||
AttributeConsts.LeMask => "Le",
|
|
||||||
AttributeConsts.LtMask => "Lt",
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
|
|
||||||
if (subgroupMask != null)
|
|
||||||
{
|
|
||||||
return config.GpuAccessor.QueryHostSupportsShaderBallot()
|
|
||||||
? $"unpackUint2x32(gl_SubGroup{subgroupMask}MaskARB).x"
|
|
||||||
: $"gl_Subgroup{subgroupMask}Mask.x";
|
|
||||||
}
|
|
||||||
else if (value == AttributeConsts.LaneId)
|
|
||||||
{
|
|
||||||
return config.GpuAccessor.QueryHostSupportsShaderBallot()
|
|
||||||
? "gl_SubGroupInvocationARB"
|
|
||||||
: "gl_SubgroupInvocationID";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
// TODO: There must be a better way to handle this...
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case AttributeConsts.PositionX: return $"(gl_FragCoord.x / {DefaultNames.SupportBlockRenderScaleName}[0])";
|
|
||||||
case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])";
|
|
||||||
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
|
|
||||||
case AttributeConsts.PositionW: return "gl_FragCoord.w";
|
|
||||||
|
|
||||||
case AttributeConsts.FrontFacing:
|
|
||||||
if (config.GpuAccessor.QueryHostHasFrontFacingBug())
|
|
||||||
{
|
|
||||||
// This is required for Intel on Windows, gl_FrontFacing sometimes returns incorrect
|
|
||||||
// (flipped) values. Doing this seems to fix it.
|
|
||||||
return "(-floatBitsToInt(float(gl_FrontFacing)) < 0)";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string name = builtInAttr.Name;
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value))
|
|
||||||
{
|
|
||||||
name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Warn about unknown built-in attribute.
|
|
||||||
|
|
||||||
return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetAttributeName(string attrExpr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0")
|
|
||||||
{
|
|
||||||
string name = isOutAttr
|
|
||||||
? DefaultNames.OAttributePrefix
|
|
||||||
: DefaultNames.IAttributePrefix;
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
|
||||||
{
|
|
||||||
name += $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
||||||
{
|
{
|
||||||
if (cbIndexable)
|
if (cbIndexable)
|
||||||
@@ -387,12 +139,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
{
|
{
|
||||||
int index = (int)stage;
|
int index = (int)stage;
|
||||||
|
|
||||||
if ((uint)index >= StagePrefixes.Length)
|
if ((uint)index >= _stagePrefixes.Length)
|
||||||
{
|
{
|
||||||
return "invalid";
|
return "invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
return StagePrefixes[index];
|
return _stagePrefixes[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char GetSwizzleMask(int value)
|
private static char GetSwizzleMask(int value)
|
||||||
@@ -405,24 +157,54 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}";
|
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false)
|
public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node)
|
||||||
{
|
{
|
||||||
|
// TODO: Get rid of that function entirely and return the type from the operation generation
|
||||||
|
// functions directly, like SPIR-V does.
|
||||||
|
|
||||||
if (node is AstOperation operation)
|
if (node is AstOperation operation)
|
||||||
{
|
{
|
||||||
if (operation.Inst == Instruction.LoadAttribute)
|
if (operation.Inst == Instruction.Load)
|
||||||
{
|
{
|
||||||
// Load attribute basically just returns the attribute value.
|
switch (operation.StorageKind)
|
||||||
// Some built-in attributes may have different types, so we need
|
|
||||||
// to return the type based on the attribute that is being read.
|
|
||||||
if (operation.GetSource(0) is AstOperand operand && operand.Type == OperandType.Constant)
|
|
||||||
{
|
{
|
||||||
if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr))
|
case StorageKind.Input:
|
||||||
|
case StorageKind.InputPerPatch:
|
||||||
|
case StorageKind.Output:
|
||||||
|
case StorageKind.OutputPerPatch:
|
||||||
|
if (!(operation.GetSource(0) is AstOperand varId) || varId.Type != OperandType.Constant)
|
||||||
{
|
{
|
||||||
return builtInAttr.Type;
|
throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IoVariable ioVariable = (IoVariable)varId.Value;
|
||||||
|
bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch;
|
||||||
|
bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch;
|
||||||
|
int location = 0;
|
||||||
|
int component = 0;
|
||||||
|
|
||||||
|
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||||
|
{
|
||||||
|
if (!(operation.GetSource(1) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
location = vecIndex.Value;
|
||||||
|
|
||||||
|
if (operation.SourcesCount > 2 &&
|
||||||
|
operation.GetSource(2) is AstOperand elemIndex &&
|
||||||
|
elemIndex.Type == OperandType.Constant &&
|
||||||
|
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput))
|
||||||
|
{
|
||||||
|
component = elemIndex.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OperandInfo.GetVarType(OperandType.Attribute);
|
(_, AggregateType varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch);
|
||||||
|
|
||||||
|
return varType & AggregateType.ElementTypeMask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (operation.Inst == Instruction.Call)
|
else if (operation.Inst == Instruction.Call)
|
||||||
{
|
{
|
||||||
@@ -461,45 +243,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
return context.CurrentFunction.GetArgumentType(argIndex);
|
return context.CurrentFunction.GetArgumentType(argIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetOperandVarType(context, operand, isAsgDest);
|
return OperandInfo.GetVarType(operand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
|
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AggregateType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false)
|
|
||||||
{
|
|
||||||
if (operand.Type == OperandType.Attribute)
|
|
||||||
{
|
|
||||||
if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr))
|
|
||||||
{
|
|
||||||
return builtInAttr.Type;
|
|
||||||
}
|
|
||||||
else if (context.Config.Stage == ShaderStage.Vertex && !isAsgDest &&
|
|
||||||
operand.Value >= AttributeConsts.UserAttributeBase &&
|
|
||||||
operand.Value < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
int location = (operand.Value - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location);
|
|
||||||
|
|
||||||
return type.ToAggregateType();
|
|
||||||
}
|
|
||||||
else if (context.Config.Stage == ShaderStage.Fragment && isAsgDest &&
|
|
||||||
operand.Value >= AttributeConsts.FragmentOutputColorBase &&
|
|
||||||
operand.Value < AttributeConsts.FragmentOutputColorEnd)
|
|
||||||
{
|
|
||||||
int location = (operand.Value - AttributeConsts.FragmentOutputColorBase) / 16;
|
|
||||||
|
|
||||||
AttributeType type = context.Config.GpuAccessor.QueryFragmentOutputType(location);
|
|
||||||
|
|
||||||
return type.ToAggregateType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return OperandInfo.GetVarType(operand);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -29,15 +29,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
public Instruction StorageBuffersArray { get; set; }
|
public Instruction StorageBuffersArray { get; set; }
|
||||||
public Instruction LocalMemory { get; set; }
|
public Instruction LocalMemory { get; set; }
|
||||||
public Instruction SharedMemory { get; set; }
|
public Instruction SharedMemory { get; set; }
|
||||||
public Instruction InputsArray { get; set; }
|
|
||||||
public Instruction OutputsArray { get; set; }
|
|
||||||
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
||||||
public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>();
|
public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>();
|
||||||
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
|
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
|
||||||
public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, Instruction>();
|
public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||||
public Dictionary<int, Instruction> Outputs { get; } = new Dictionary<int, Instruction>();
|
public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||||
public Dictionary<int, Instruction> InputsPerPatch { get; } = new Dictionary<int, Instruction>();
|
public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||||
public Dictionary<int, Instruction> OutputsPerPatch { get; } = new Dictionary<int, Instruction>();
|
public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||||
|
|
||||||
public Instruction CoordTemp { get; set; }
|
public Instruction CoordTemp { get; set; }
|
||||||
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
||||||
@@ -163,16 +161,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
mainInterface.AddRange(InputsPerPatch.Values);
|
mainInterface.AddRange(InputsPerPatch.Values);
|
||||||
mainInterface.AddRange(OutputsPerPatch.Values);
|
mainInterface.AddRange(OutputsPerPatch.Values);
|
||||||
|
|
||||||
if (InputsArray != null)
|
|
||||||
{
|
|
||||||
mainInterface.Add(InputsArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OutputsArray != null)
|
|
||||||
{
|
|
||||||
mainInterface.Add(OutputsArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mainInterface.ToArray();
|
return mainInterface.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,8 +216,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return operand.Type switch
|
return operand.Type switch
|
||||||
{
|
{
|
||||||
IrOperandType.Argument => GetArgument(type, operand),
|
IrOperandType.Argument => GetArgument(type, operand),
|
||||||
IrOperandType.Attribute => GetAttribute(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0),
|
|
||||||
IrOperandType.AttributePerPatch => GetAttributePerPatch(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0),
|
|
||||||
IrOperandType.Constant => GetConstant(type, operand),
|
IrOperandType.Constant => GetConstant(type, operand),
|
||||||
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
|
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
|
||||||
IrOperandType.LocalVariable => GetLocal(type, operand),
|
IrOperandType.LocalVariable => GetLocal(type, operand),
|
||||||
@@ -275,239 +261,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction GetAttributeElemPointer(int attr, bool isOutAttr, Instruction index, out AggregateType elemType)
|
|
||||||
{
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
var attrInfo = AttributeInfo.From(Config, attr, isOutAttr);
|
|
||||||
|
|
||||||
int attrOffset = attrInfo.BaseValue;
|
|
||||||
AggregateType type = attrInfo.Type;
|
|
||||||
|
|
||||||
Instruction ioVariable, elemIndex;
|
|
||||||
|
|
||||||
Instruction invocationId = null;
|
|
||||||
|
|
||||||
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
|
||||||
{
|
|
||||||
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
|
|
||||||
if (isUserAttr &&
|
|
||||||
((!isOutAttr && Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
|
|
||||||
(isOutAttr && Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))))
|
|
||||||
{
|
|
||||||
elemType = AggregateType.FP32;
|
|
||||||
ioVariable = isOutAttr ? OutputsArray : InputsArray;
|
|
||||||
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
|
||||||
var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4);
|
|
||||||
|
|
||||||
bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
|
|
||||||
|
|
||||||
if (invocationId != null && isArray)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
else if (invocationId != null)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
else if (isArray)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isViewportInverse = attr == AttributeConsts.SupportBlockViewInverseX || attr == AttributeConsts.SupportBlockViewInverseY;
|
|
||||||
|
|
||||||
if (isViewportInverse)
|
|
||||||
{
|
|
||||||
elemType = AggregateType.FP32;
|
|
||||||
elemIndex = Constant(TypeU32(), (attr - AttributeConsts.SupportBlockViewInverseX) >> 2);
|
|
||||||
return AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), SupportBuffer, Constant(TypeU32(), 2), elemIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
|
||||||
|
|
||||||
if (isUserAttr && Config.TransformFeedbackEnabled &&
|
|
||||||
((isOutAttr && Config.LastInVertexPipeline) ||
|
|
||||||
(!isOutAttr && Config.Stage == ShaderStage.Fragment)))
|
|
||||||
{
|
|
||||||
attrOffset = attr;
|
|
||||||
type = elemType;
|
|
||||||
|
|
||||||
if (Config.LastInPipeline && isOutAttr)
|
|
||||||
{
|
|
||||||
int components = Info.GetTransformFeedbackOutputComponents(attr);
|
|
||||||
|
|
||||||
if (components > 1)
|
|
||||||
{
|
|
||||||
attrOffset &= ~0xf;
|
|
||||||
type = components switch
|
|
||||||
{
|
|
||||||
2 => AggregateType.Vector2 | AggregateType.FP32,
|
|
||||||
3 => AggregateType.Vector3 | AggregateType.FP32,
|
|
||||||
4 => AggregateType.Vector4 | AggregateType.FP32,
|
|
||||||
_ => AggregateType.FP32
|
|
||||||
};
|
|
||||||
|
|
||||||
attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset];
|
|
||||||
|
|
||||||
bool isIndexed = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr));
|
|
||||||
|
|
||||||
if ((type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0)
|
|
||||||
{
|
|
||||||
if (invocationId != null)
|
|
||||||
{
|
|
||||||
return isIndexed
|
|
||||||
? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index)
|
|
||||||
: AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
|
||||||
|
|
||||||
if (invocationId != null && isIndexed)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex);
|
|
||||||
}
|
|
||||||
else if (invocationId != null)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex);
|
|
||||||
}
|
|
||||||
else if (isIndexed)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetAttributeElemPointer(Instruction attrIndex, bool isOutAttr, Instruction index, out AggregateType elemType)
|
|
||||||
{
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
|
|
||||||
Instruction invocationId = null;
|
|
||||||
|
|
||||||
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
|
||||||
{
|
|
||||||
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
elemType = AggregateType.FP32;
|
|
||||||
var ioVariable = isOutAttr ? OutputsArray : InputsArray;
|
|
||||||
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
|
|
||||||
var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3));
|
|
||||||
|
|
||||||
bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
|
|
||||||
|
|
||||||
if (invocationId != null && isArray)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
else if (invocationId != null)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
else if (isArray)
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetAttribute(AggregateType type, int attr, bool isOutAttr, Instruction index = null)
|
|
||||||
{
|
|
||||||
if (!AttributeInfo.Validate(Config, attr, isOutAttr: false))
|
|
||||||
{
|
|
||||||
return GetConstant(type, new AstOperand(IrOperandType.Constant, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType);
|
|
||||||
var value = Load(GetType(elemType), elemPointer);
|
|
||||||
|
|
||||||
if (Config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
if (attr == AttributeConsts.PositionX || attr == AttributeConsts.PositionY)
|
|
||||||
{
|
|
||||||
var pointerType = TypePointer(StorageClass.Uniform, TypeFP32());
|
|
||||||
var fieldIndex = Constant(TypeU32(), 4);
|
|
||||||
var scaleIndex = Constant(TypeU32(), 0);
|
|
||||||
|
|
||||||
var scaleElemPointer = AccessChain(pointerType, SupportBuffer, fieldIndex, scaleIndex);
|
|
||||||
var scale = Load(TypeFP32(), scaleElemPointer);
|
|
||||||
|
|
||||||
value = FDiv(TypeFP32(), value, scale);
|
|
||||||
}
|
|
||||||
else if (attr == AttributeConsts.FrontFacing && Config.GpuAccessor.QueryHostHasFrontFacingBug())
|
|
||||||
{
|
|
||||||
// Workaround for what appears to be a bug on Intel compiler.
|
|
||||||
var valueFloat = Select(TypeFP32(), value, Constant(TypeFP32(), 1f), Constant(TypeFP32(), 0f));
|
|
||||||
var valueAsInt = Bitcast(TypeS32(), valueFloat);
|
|
||||||
var valueNegated = SNegate(TypeS32(), valueAsInt);
|
|
||||||
|
|
||||||
value = SLessThan(TypeBool(), valueNegated, Constant(TypeS32(), 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return BitcastIfNeeded(type, elemType, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)
|
|
||||||
{
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr);
|
|
||||||
|
|
||||||
int attrOffset = attrInfo.BaseValue;
|
|
||||||
Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
|
|
||||||
|
|
||||||
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
|
||||||
|
|
||||||
if ((attrInfo.Type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0)
|
|
||||||
{
|
|
||||||
return ioVariable;
|
|
||||||
}
|
|
||||||
|
|
||||||
var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (!AttributeInfo.ValidatePerPatch(Config, attr, isOutAttr: false))
|
|
||||||
{
|
|
||||||
return GetConstant(type, new AstOperand(IrOperandType.Constant, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
var elemPointer = GetAttributePerPatchElemPointer(attr, isOutAttr, out var elemType);
|
|
||||||
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetAttribute(AggregateType type, Instruction attr, bool isOutAttr, Instruction index = null)
|
|
||||||
{
|
|
||||||
var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType);
|
|
||||||
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetConstant(AggregateType type, AstOperand operand)
|
public Instruction GetConstant(AggregateType type, AstOperand operand)
|
||||||
{
|
{
|
||||||
return type switch
|
return type switch
|
||||||
|
@@ -1,21 +1,19 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using Spv.Generator;
|
using Spv.Generator;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
using SpvInstruction = Spv.Generator.Instruction;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
{
|
{
|
||||||
static class Declarations
|
static class Declarations
|
||||||
{
|
{
|
||||||
// At least 16 attributes are guaranteed by the spec.
|
|
||||||
public const int MaxAttributes = 16;
|
|
||||||
|
|
||||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||||
|
|
||||||
public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
|
public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
|
||||||
@@ -59,7 +57,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
||||||
{
|
{
|
||||||
StructuredFunction function = functions[funcIndex];
|
StructuredFunction function = functions[funcIndex];
|
||||||
Instruction[] locals = new Instruction[function.InArguments.Length];
|
SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
|
||||||
|
|
||||||
for (int i = 0; i < function.InArguments.Length; i++)
|
for (int i = 0; i < function.InArguments.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -105,10 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
||||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||||
DeclareInputAttributes(context, info, perPatch: false);
|
DeclareInputsAndOutputs(context, info);
|
||||||
DeclareOutputAttributes(context, info, perPatch: false);
|
|
||||||
DeclareInputAttributes(context, info, perPatch: true);
|
|
||||||
DeclareOutputAttributes(context, info, perPatch: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareLocalMemory(CodeGenContext context, int size)
|
private static void DeclareLocalMemory(CodeGenContext context, int size)
|
||||||
@@ -121,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
|
context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Instruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
|
private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
|
||||||
{
|
{
|
||||||
var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
|
var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
|
||||||
var pointerType = context.TypePointer(storage, arrayType);
|
var pointerType = context.TypePointer(storage, arrayType);
|
||||||
@@ -395,164 +390,104 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info)
|
||||||
{
|
{
|
||||||
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
|
foreach (var ioDefinition in info.IoDefinitions)
|
||||||
|
|
||||||
if (iaIndexing && !perPatch)
|
|
||||||
{
|
{
|
||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
var ioVariable = ioDefinition.IoVariable;
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Geometry)
|
// Those are actually from constant buffer, rather than being actual inputs or outputs,
|
||||||
{
|
// so we must ignore them here as they are declared as part of the support buffer.
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
|
// TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input").
|
||||||
}
|
if (ioVariable == IoVariable.FragmentOutputIsBgra ||
|
||||||
|
ioVariable == IoVariable.SupportBlockRenderScale ||
|
||||||
var spvType = context.TypePointer(StorageClass.Input, attrType);
|
ioVariable == IoVariable.SupportBlockViewInverse)
|
||||||
var spvVar = context.Variable(spvType, StorageClass.Input);
|
|
||||||
|
|
||||||
if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
|
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
|
||||||
context.InputsArray = spvVar;
|
|
||||||
}
|
|
||||||
|
|
||||||
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
|
|
||||||
|
|
||||||
foreach (int attr in inputs)
|
|
||||||
{
|
|
||||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
bool isOutput = ioDefinition.StorageKind.IsOutput();
|
||||||
|
bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
|
||||||
if (iaIndexing && isUserAttr && !perPatch)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PixelImap iq = PixelImap.Unused;
|
PixelImap iq = PixelImap.Unused;
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
if (context.Config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
if (ioVariable == IoVariable.UserDefined)
|
||||||
{
|
{
|
||||||
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
|
iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
|
(_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||||
AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
AggregateType elemType = varType & AggregateType.ElementTypeMask;
|
||||||
|
|
||||||
if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
|
if (elemType == AggregateType.S32 || elemType == AggregateType.U32)
|
||||||
{
|
{
|
||||||
iq = PixelImap.Constant;
|
iq = PixelImap.Constant;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
|
DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
private static void DeclareInputOrOutput(CodeGenContext context, IoDefinition ioDefinition, bool isOutput, bool isPerPatch, PixelImap iq = PixelImap.Unused)
|
||||||
{
|
{
|
||||||
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
|
IoVariable ioVariable = ioDefinition.IoVariable;
|
||||||
|
var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
||||||
|
|
||||||
if (oaIndexing && !perPatch)
|
bool isBuiltIn;
|
||||||
{
|
BuiltIn builtIn = default;
|
||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
AggregateType varType;
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.TessellationControl)
|
if (ioVariable == IoVariable.UserDefined)
|
||||||
{
|
{
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput);
|
||||||
|
isBuiltIn = false;
|
||||||
}
|
}
|
||||||
|
else if (ioVariable == IoVariable.FragmentOutputColor)
|
||||||
var spvType = context.TypePointer(StorageClass.Output, attrType);
|
|
||||||
var spvVar = context.Variable(spvType, StorageClass.Output);
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
|
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
|
||||||
context.OutputsArray = spvVar;
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
|
|
||||||
|
|
||||||
foreach (int attr in outputs)
|
|
||||||
{
|
{
|
||||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
|
varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location);
|
||||||
{
|
isBuiltIn = false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
|
|
||||||
if (oaIndexing && isUserAttr && !perPatch)
|
|
||||||
{
|
{
|
||||||
continue;
|
(builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||||
}
|
isBuiltIn = true;
|
||||||
|
|
||||||
DeclareOutputAttribute(context, attr, perPatch);
|
if (varType == AggregateType.Invalid)
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
{
|
||||||
DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false);
|
throw new InvalidOperationException($"Unknown variable {ioVariable}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch)
|
bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput);
|
||||||
|
|
||||||
|
if (hasComponent)
|
||||||
{
|
{
|
||||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true);
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
}
|
||||||
|
else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput))
|
||||||
|
{
|
||||||
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch
|
||||||
|
{
|
||||||
|
2 => AggregateType.Vector2,
|
||||||
|
3 => AggregateType.Vector3,
|
||||||
|
4 => AggregateType.Vector4,
|
||||||
|
_ => AggregateType.Invalid
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DeclareInvocationId(CodeGenContext context)
|
var spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable));
|
||||||
{
|
|
||||||
DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
|
||||||
{
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch &&
|
|
||||||
((isOutAttr && context.Config.LastInVertexPipeline) ||
|
|
||||||
(!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
|
|
||||||
{
|
|
||||||
DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dict = perPatch
|
|
||||||
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
|
|
||||||
: (isOutAttr ? context.Outputs : context.Inputs);
|
|
||||||
|
|
||||||
var attrInfo = perPatch
|
|
||||||
? AttributeInfo.FromPatch(context.Config, attr, isOutAttr)
|
|
||||||
: AttributeInfo.From(context.Config, attr, isOutAttr);
|
|
||||||
|
|
||||||
if (dict.ContainsKey(attrInfo.BaseValue))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
var attrType = context.GetType(attrInfo.Type, attrInfo.Length);
|
|
||||||
bool builtInPassthrough = false;
|
bool builtInPassthrough = false;
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && !perPatch && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput))
|
||||||
{
|
{
|
||||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
||||||
|
|
||||||
if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||||
{
|
{
|
||||||
@@ -560,69 +495,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
|
if (context.Config.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch)
|
||||||
{
|
{
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||||
}
|
}
|
||||||
|
|
||||||
var spvType = context.TypePointer(storageClass, attrType);
|
var spvPointerType = context.TypePointer(storageClass, spvType);
|
||||||
var spvVar = context.Variable(spvType, storageClass);
|
var spvVar = context.Variable(spvPointerType, storageClass);
|
||||||
|
|
||||||
if (builtInPassthrough)
|
if (builtInPassthrough)
|
||||||
{
|
{
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrInfo.IsBuiltin)
|
if (isBuiltIn)
|
||||||
{
|
{
|
||||||
if (perPatch)
|
if (isPerPatch)
|
||||||
{
|
{
|
||||||
context.Decorate(spvVar, Decoration.Patch);
|
context.Decorate(spvVar, Decoration.Patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
|
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && ioVariable == IoVariable.Position)
|
||||||
{
|
{
|
||||||
context.Decorate(spvVar, Decoration.Invariant);
|
context.Decorate(spvVar, Decoration.Invariant);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
|
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)builtIn);
|
||||||
|
|
||||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
|
|
||||||
{
|
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
|
|
||||||
if (tfOutput.Valid)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
|
||||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
|
|
||||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
|
|
||||||
}
|
}
|
||||||
}
|
else if (isPerPatch)
|
||||||
}
|
|
||||||
else if (perPatch)
|
|
||||||
{
|
{
|
||||||
context.Decorate(spvVar, Decoration.Patch);
|
context.Decorate(spvVar, Decoration.Patch);
|
||||||
|
|
||||||
int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16);
|
if (ioVariable == IoVariable.UserDefined)
|
||||||
|
{
|
||||||
|
int location = context.Config.GetPerPatchAttributeLocation(ioDefinition.Location);
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||||
}
|
}
|
||||||
else if (isUserAttr)
|
}
|
||||||
|
else if (ioVariable == IoVariable.UserDefined)
|
||||||
{
|
{
|
||||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)ioDefinition.Location);
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
if (hasComponent)
|
||||||
|
{
|
||||||
|
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)ioDefinition.Component);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isOutAttr &&
|
if (!isOutput &&
|
||||||
!perPatch &&
|
!isPerPatch &&
|
||||||
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
(context.Config.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 &&
|
||||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||||
{
|
{
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
else if (ioVariable == IoVariable.FragmentOutputColor)
|
||||||
{
|
{
|
||||||
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
int location = ioDefinition.Location;
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
||||||
{
|
{
|
||||||
@@ -646,7 +576,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOutAttr)
|
if (!isOutput)
|
||||||
{
|
{
|
||||||
switch (iq)
|
switch (iq)
|
||||||
{
|
{
|
||||||
@@ -658,143 +588,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (context.Config.TryGetTransformFeedbackOutput(
|
||||||
context.AddGlobalVariable(spvVar);
|
ioVariable,
|
||||||
dict.Add(attrInfo.BaseValue, spvVar);
|
ioDefinition.Location,
|
||||||
}
|
ioDefinition.Component,
|
||||||
|
out var transformFeedbackOutput))
|
||||||
private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
|
||||||
{
|
{
|
||||||
var dict = isOutAttr ? context.Outputs : context.Inputs;
|
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer);
|
||||||
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
|
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride);
|
||||||
|
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset);
|
||||||
bool hasComponent = true;
|
|
||||||
int component = (attr >> 2) & 3;
|
|
||||||
int components = 1;
|
|
||||||
var type = attrInfo.Type & AggregateType.ElementTypeMask;
|
|
||||||
|
|
||||||
if (context.Config.LastInPipeline && isOutAttr)
|
|
||||||
{
|
|
||||||
components = context.Info.GetTransformFeedbackOutputComponents(attr);
|
|
||||||
|
|
||||||
if (components > 1)
|
|
||||||
{
|
|
||||||
attr &= ~0xf;
|
|
||||||
type = components switch
|
|
||||||
{
|
|
||||||
2 => AggregateType.Vector2 | AggregateType.FP32,
|
|
||||||
3 => AggregateType.Vector3 | AggregateType.FP32,
|
|
||||||
4 => AggregateType.Vector4 | AggregateType.FP32,
|
|
||||||
_ => AggregateType.FP32
|
|
||||||
};
|
|
||||||
|
|
||||||
hasComponent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dict.ContainsKey(attr))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
var attrType = context.GetType(type, components);
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
|
||||||
{
|
|
||||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
|
||||||
{
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
|
||||||
}
|
|
||||||
|
|
||||||
var spvType = context.TypePointer(storageClass, attrType);
|
|
||||||
var spvVar = context.Variable(spvType, storageClass);
|
|
||||||
|
|
||||||
Debug.Assert(attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd);
|
|
||||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
|
||||||
|
|
||||||
if (hasComponent)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOutAttr)
|
|
||||||
{
|
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
|
|
||||||
if (tfOutput.Valid)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
|
||||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
|
|
||||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
|
||||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (iq)
|
|
||||||
{
|
|
||||||
case PixelImap.Constant:
|
|
||||||
context.Decorate(spvVar, Decoration.Flat);
|
|
||||||
break;
|
|
||||||
case PixelImap.ScreenLinear:
|
|
||||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
context.AddGlobalVariable(spvVar);
|
||||||
dict.Add(attr, spvVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BuiltIn GetBuiltIn(CodeGenContext context, int attr)
|
var dict = isPerPatch
|
||||||
{
|
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||||
return attr switch
|
: (isOutput ? context.Outputs : context.Inputs);
|
||||||
{
|
dict.Add(ioDefinition, spvVar);
|
||||||
AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
|
|
||||||
AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
|
|
||||||
AttributeConsts.Layer => BuiltIn.Layer,
|
|
||||||
AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
|
|
||||||
AttributeConsts.PointSize => BuiltIn.PointSize,
|
|
||||||
AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position,
|
|
||||||
AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
|
|
||||||
AttributeConsts.PointCoordX => BuiltIn.PointCoord,
|
|
||||||
AttributeConsts.TessCoordX => BuiltIn.TessCoord,
|
|
||||||
AttributeConsts.InstanceId => BuiltIn.InstanceId,
|
|
||||||
AttributeConsts.VertexId => BuiltIn.VertexId,
|
|
||||||
AttributeConsts.BaseInstance => BuiltIn.BaseInstance,
|
|
||||||
AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
|
|
||||||
AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
|
|
||||||
AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
|
|
||||||
AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
|
|
||||||
AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
|
|
||||||
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
|
|
||||||
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
|
|
||||||
AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId,
|
|
||||||
AttributeConsts.CtaIdX => BuiltIn.WorkgroupId,
|
|
||||||
AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId,
|
|
||||||
AttributeConsts.InvocationId => BuiltIn.InvocationId,
|
|
||||||
AttributeConsts.PrimitiveId => BuiltIn.PrimitiveId,
|
|
||||||
AttributeConsts.PatchVerticesIn => BuiltIn.PatchVertices,
|
|
||||||
AttributeConsts.EqMask => BuiltIn.SubgroupEqMask,
|
|
||||||
AttributeConsts.GeMask => BuiltIn.SubgroupGeMask,
|
|
||||||
AttributeConsts.GtMask => BuiltIn.SubgroupGtMask,
|
|
||||||
AttributeConsts.LeMask => BuiltIn.SubgroupLeMask,
|
|
||||||
AttributeConsts.LtMask => BuiltIn.SubgroupLtMask,
|
|
||||||
AttributeConsts.SupportBlockViewInverseX => BuiltIn.Position,
|
|
||||||
AttributeConsts.SupportBlockViewInverseY => BuiltIn.Position,
|
|
||||||
_ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetStagePrefix(ShaderStage stage)
|
private static string GetStagePrefix(ShaderStage stage)
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Graphics.Shader.Translation;
|
using System;
|
||||||
using System;
|
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
|
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.ImageLoad, GenerateImageLoad);
|
Add(Instruction.ImageLoad, GenerateImageLoad);
|
||||||
Add(Instruction.ImageStore, GenerateImageStore);
|
Add(Instruction.ImageStore, GenerateImageStore);
|
||||||
Add(Instruction.IsNan, GenerateIsNan);
|
Add(Instruction.IsNan, GenerateIsNan);
|
||||||
Add(Instruction.LoadAttribute, GenerateLoadAttribute);
|
Add(Instruction.Load, GenerateLoad);
|
||||||
Add(Instruction.LoadConstant, GenerateLoadConstant);
|
Add(Instruction.LoadConstant, GenerateLoadConstant);
|
||||||
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
||||||
Add(Instruction.LoadShared, GenerateLoadShared);
|
Add(Instruction.LoadShared, GenerateLoadShared);
|
||||||
@@ -132,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.ShuffleXor, GenerateShuffleXor);
|
Add(Instruction.ShuffleXor, GenerateShuffleXor);
|
||||||
Add(Instruction.Sine, GenerateSine);
|
Add(Instruction.Sine, GenerateSine);
|
||||||
Add(Instruction.SquareRoot, GenerateSquareRoot);
|
Add(Instruction.SquareRoot, GenerateSquareRoot);
|
||||||
Add(Instruction.StoreAttribute, GenerateStoreAttribute);
|
Add(Instruction.Store, GenerateStore);
|
||||||
Add(Instruction.StoreLocal, GenerateStoreLocal);
|
Add(Instruction.StoreLocal, GenerateStoreLocal);
|
||||||
Add(Instruction.StoreShared, GenerateStoreShared);
|
Add(Instruction.StoreShared, GenerateStoreShared);
|
||||||
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
||||||
@@ -861,31 +862,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateLoadAttribute(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateLoad(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var src1 = operation.GetSource(0);
|
return GenerateLoadOrStore(context, operation, isStore: false);
|
||||||
var src2 = operation.GetSource(1);
|
|
||||||
var src3 = operation.GetSource(2);
|
|
||||||
|
|
||||||
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = context.Get(AggregateType.S32, src3);
|
|
||||||
var resultType = AggregateType.FP32;
|
|
||||||
|
|
||||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
|
||||||
{
|
|
||||||
int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2);
|
|
||||||
bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0;
|
|
||||||
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var attr = context.Get(AggregateType.S32, src2);
|
|
||||||
return new OperationResult(resultType, context.GetAttribute(resultType, attr, isOutAttr: false, index));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation)
|
||||||
@@ -1223,7 +1202,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
||||||
var indexNotSegMask = context.BitwiseAnd(context.TypeU32(), index, notSegMask);
|
var indexNotSegMask = context.BitwiseAnd(context.TypeU32(), index, notSegMask);
|
||||||
|
|
||||||
var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false);
|
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
@@ -1253,7 +1232,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var notSegMask = context.Not(context.TypeU32(), segMask);
|
var notSegMask = context.Not(context.TypeU32(), segMask);
|
||||||
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
||||||
|
|
||||||
var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false);
|
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
@@ -1280,7 +1259,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31);
|
var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31);
|
||||||
|
|
||||||
var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false);
|
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
||||||
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
||||||
@@ -1309,7 +1288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var notSegMask = context.Not(context.TypeU32(), segMask);
|
var notSegMask = context.Not(context.TypeU32(), segMask);
|
||||||
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
||||||
|
|
||||||
var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false);
|
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
@@ -1335,35 +1314,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return GenerateUnary(context, operation, context.Delegates.GlslSqrt, null);
|
return GenerateUnary(context, operation, context.Delegates.GlslSqrt, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateStoreAttribute(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateStore(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var src1 = operation.GetSource(0);
|
return GenerateLoadOrStore(context, operation, isStore: true);
|
||||||
var src2 = operation.GetSource(1);
|
|
||||||
var src3 = operation.GetSource(2);
|
|
||||||
|
|
||||||
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"First input of {nameof(Instruction.StoreAttribute)} must be a constant operand.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SpvInstruction elemPointer;
|
|
||||||
AggregateType elemType;
|
|
||||||
|
|
||||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
|
||||||
{
|
|
||||||
int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2);
|
|
||||||
elemPointer = context.GetAttributeElemPointer(attrOffset, isOutAttr: true, index: null, out elemType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var attr = context.Get(AggregateType.S32, src2);
|
|
||||||
elemPointer = context.GetAttributeElemPointer(attr, isOutAttr: true, index: null, out elemType);
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = context.Get(elemType, src3);
|
|
||||||
context.Store(elemPointer, value);
|
|
||||||
|
|
||||||
return OperationResult.Invalid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation)
|
||||||
@@ -1447,7 +1400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
var three = context.Constant(context.TypeU32(), 3);
|
var three = context.Constant(context.TypeU32(), 3);
|
||||||
|
|
||||||
var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false);
|
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
||||||
var shift = context.BitwiseAnd(context.TypeU32(), threadId, three);
|
var shift = context.BitwiseAnd(context.TypeU32(), threadId, three);
|
||||||
shift = context.ShiftLeftLogical(context.TypeU32(), shift, context.Constant(context.TypeU32(), 1));
|
shift = context.ShiftLeftLogical(context.TypeU32(), shift, context.Constant(context.TypeU32(), 1));
|
||||||
var lutIdx = context.ShiftRightLogical(context.TypeU32(), mask, shift);
|
var lutIdx = context.ShiftRightLogical(context.TypeU32(), mask, shift);
|
||||||
@@ -1556,6 +1509,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image)
|
||||||
|
{
|
||||||
|
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
|
||||||
|
if (isGather && gatherBiasPrecision != 0)
|
||||||
|
{
|
||||||
|
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
|
||||||
|
// Offset by the gather precision divided by 2 to correct for rounding.
|
||||||
|
var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount);
|
||||||
|
var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount);
|
||||||
|
|
||||||
|
var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1)));
|
||||||
|
var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray());
|
||||||
|
|
||||||
|
var one = context.Constant(context.TypeFP32(), 1f);
|
||||||
|
var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray());
|
||||||
|
|
||||||
|
var divisor = context.FMul(
|
||||||
|
pVectorType,
|
||||||
|
context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)),
|
||||||
|
biasVector);
|
||||||
|
|
||||||
|
vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
SpvInstruction pCoords = AssemblePVector(pCount);
|
SpvInstruction pCoords = AssemblePVector(pCount);
|
||||||
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount);
|
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount);
|
||||||
|
|
||||||
@@ -1716,6 +1696,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
image = context.Image(imageType, image);
|
image = context.Image(imageType, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pCoords = ApplyBias(pCoords, image);
|
||||||
|
|
||||||
var operands = operandsList.ToArray();
|
var operands = operandsList.ToArray();
|
||||||
|
|
||||||
SpvInstruction result;
|
SpvInstruction result;
|
||||||
@@ -1952,20 +1934,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var value = context.GetU32(operation.GetSource(2));
|
var value = context.GetU32(operation.GetSource(2));
|
||||||
|
|
||||||
SpvInstruction elemPointer;
|
SpvInstruction elemPointer;
|
||||||
Instruction mr = operation.Inst & Instruction.MrMask;
|
|
||||||
|
|
||||||
if (mr == Instruction.MrStorage)
|
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
{
|
{
|
||||||
elemPointer = GetStorageElemPointer(context, operation);
|
elemPointer = GetStorageElemPointer(context, operation);
|
||||||
}
|
}
|
||||||
else if (mr == Instruction.MrShared)
|
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||||
{
|
{
|
||||||
var offset = context.GetU32(operation.GetSource(0));
|
var offset = context.GetU32(operation.GetSource(0));
|
||||||
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset);
|
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Invalid storage class \"{mr}\".");
|
throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
var one = context.Constant(context.TypeU32(), 1);
|
var one = context.Constant(context.TypeU32(), 1);
|
||||||
@@ -1980,20 +1961,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var value1 = context.GetU32(operation.GetSource(3));
|
var value1 = context.GetU32(operation.GetSource(3));
|
||||||
|
|
||||||
SpvInstruction elemPointer;
|
SpvInstruction elemPointer;
|
||||||
Instruction mr = operation.Inst & Instruction.MrMask;
|
|
||||||
|
|
||||||
if (mr == Instruction.MrStorage)
|
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
{
|
{
|
||||||
elemPointer = GetStorageElemPointer(context, operation);
|
elemPointer = GetStorageElemPointer(context, operation);
|
||||||
}
|
}
|
||||||
else if (mr == Instruction.MrShared)
|
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||||
{
|
{
|
||||||
var offset = context.GetU32(operation.GetSource(0));
|
var offset = context.GetU32(operation.GetSource(0));
|
||||||
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset);
|
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Invalid storage class \"{mr}\".");
|
throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
var one = context.Constant(context.TypeU32(), 1);
|
var one = context.Constant(context.TypeU32(), 1);
|
||||||
@@ -2002,6 +1982,163 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return new OperationResult(AggregateType.U32, context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, value1, value0));
|
return new OperationResult(AggregateType.U32, context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, value1, value0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
||||||
|
{
|
||||||
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
|
SpvInstruction pointer;
|
||||||
|
AggregateType varType;
|
||||||
|
int srcIndex = 0;
|
||||||
|
|
||||||
|
switch (storageKind)
|
||||||
|
{
|
||||||
|
case StorageKind.Input:
|
||||||
|
case StorageKind.InputPerPatch:
|
||||||
|
case StorageKind.Output:
|
||||||
|
case StorageKind.OutputPerPatch:
|
||||||
|
if (!(operation.GetSource(srcIndex++) is AstOperand varId) || varId.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IoVariable ioVariable = (IoVariable)varId.Value;
|
||||||
|
bool isOutput = storageKind.IsOutput();
|
||||||
|
bool isPerPatch = storageKind.IsPerPatch();
|
||||||
|
int location = 0;
|
||||||
|
int component = 0;
|
||||||
|
|
||||||
|
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||||
|
{
|
||||||
|
if (!(operation.GetSource(srcIndex++) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
location = vecIndex.Value;
|
||||||
|
|
||||||
|
if (operation.SourcesCount > srcIndex &&
|
||||||
|
operation.GetSource(srcIndex) is AstOperand elemIndex &&
|
||||||
|
elemIndex.Type == OperandType.Constant &&
|
||||||
|
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput))
|
||||||
|
{
|
||||||
|
component = elemIndex.Value;
|
||||||
|
srcIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioVariable == IoVariable.UserDefined)
|
||||||
|
{
|
||||||
|
varType = context.Config.GetUserDefinedType(location, isOutput);
|
||||||
|
}
|
||||||
|
else if (ioVariable == IoVariable.FragmentOutputColor)
|
||||||
|
{
|
||||||
|
varType = context.Config.GetFragmentOutputColorType(location);
|
||||||
|
}
|
||||||
|
else if (ioVariable == IoVariable.FragmentOutputIsBgra)
|
||||||
|
{
|
||||||
|
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeU32());
|
||||||
|
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 1), elemIndex);
|
||||||
|
varType = AggregateType.U32;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (ioVariable == IoVariable.SupportBlockRenderScale)
|
||||||
|
{
|
||||||
|
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
||||||
|
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 4), elemIndex);
|
||||||
|
varType = AggregateType.FP32;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (ioVariable == IoVariable.SupportBlockViewInverse)
|
||||||
|
{
|
||||||
|
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
||||||
|
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 2), elemIndex);
|
||||||
|
varType = AggregateType.FP32;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
|
||||||
|
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
|
||||||
|
var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
||||||
|
|
||||||
|
var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component);
|
||||||
|
var dict = isPerPatch
|
||||||
|
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||||
|
: (isOutput ? context.Outputs : context.Inputs);
|
||||||
|
|
||||||
|
SpvInstruction baseObj = dict[ioDefinition];
|
||||||
|
SpvInstruction e0, e1, e2;
|
||||||
|
|
||||||
|
switch (inputsCount)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
pointer = baseObj;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||||
|
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var indexes = new SpvInstruction[inputsCount];
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (; index < inputsCount; srcIndex++, index++)
|
||||||
|
{
|
||||||
|
indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStore)
|
||||||
|
{
|
||||||
|
context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex)));
|
||||||
|
return OperationResult.Invalid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = context.Load(context.GetType(varType), pointer);
|
||||||
|
return new OperationResult(varType, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
|
||||||
|
{
|
||||||
|
(_, var varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||||
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
|
||||||
|
var ioDefinition = new IoDefinition(StorageKind.Input, ioVariable);
|
||||||
|
|
||||||
|
return context.Load(context.GetType(varType), context.Inputs[ioDefinition]);
|
||||||
|
}
|
||||||
|
|
||||||
private static void GenerateStoreSharedSmallInt(CodeGenContext context, AstOperation operation, int bitSize)
|
private static void GenerateStoreSharedSmallInt(CodeGenContext context, AstOperation operation, int bitSize)
|
||||||
{
|
{
|
||||||
var offset = context.Get(AggregateType.U32, operation.GetSource(0));
|
var offset = context.Get(AggregateType.U32, operation.GetSource(0));
|
||||||
|
86
Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs
Normal file
86
Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using static Spv.Specification;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
|
{
|
||||||
|
static class IoMap
|
||||||
|
{
|
||||||
|
// At least 16 attributes are guaranteed by the spec.
|
||||||
|
private const int MaxAttributes = 16;
|
||||||
|
|
||||||
|
public static (BuiltIn, AggregateType) GetSpirvBuiltIn(IoVariable ioVariable)
|
||||||
|
{
|
||||||
|
return ioVariable switch
|
||||||
|
{
|
||||||
|
IoVariable.BaseInstance => (BuiltIn.BaseInstance, AggregateType.S32),
|
||||||
|
IoVariable.BaseVertex => (BuiltIn.BaseVertex, AggregateType.S32),
|
||||||
|
IoVariable.ClipDistance => (BuiltIn.ClipDistance, AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.CtaId => (BuiltIn.WorkgroupId, AggregateType.Vector3 | AggregateType.U32),
|
||||||
|
IoVariable.DrawIndex => (BuiltIn.DrawIndex, AggregateType.S32),
|
||||||
|
IoVariable.FragmentCoord => (BuiltIn.FragCoord, AggregateType.Vector4 | AggregateType.FP32),
|
||||||
|
IoVariable.FragmentOutputDepth => (BuiltIn.FragDepth, AggregateType.FP32),
|
||||||
|
IoVariable.FrontFacing => (BuiltIn.FrontFacing, AggregateType.Bool),
|
||||||
|
IoVariable.InstanceId => (BuiltIn.InstanceId, AggregateType.S32),
|
||||||
|
IoVariable.InstanceIndex => (BuiltIn.InstanceIndex, AggregateType.S32),
|
||||||
|
IoVariable.InvocationId => (BuiltIn.InvocationId, AggregateType.S32),
|
||||||
|
IoVariable.Layer => (BuiltIn.Layer, AggregateType.S32),
|
||||||
|
IoVariable.PatchVertices => (BuiltIn.PatchVertices, AggregateType.S32),
|
||||||
|
IoVariable.PointCoord => (BuiltIn.PointCoord, AggregateType.Vector2 | AggregateType.FP32),
|
||||||
|
IoVariable.PointSize => (BuiltIn.PointSize, AggregateType.FP32),
|
||||||
|
IoVariable.Position => (BuiltIn.Position, AggregateType.Vector4 | AggregateType.FP32),
|
||||||
|
IoVariable.PrimitiveId => (BuiltIn.PrimitiveId, AggregateType.S32),
|
||||||
|
IoVariable.SubgroupEqMask => (BuiltIn.SubgroupEqMask, AggregateType.Vector4 | AggregateType.U32),
|
||||||
|
IoVariable.SubgroupGeMask => (BuiltIn.SubgroupGeMask, AggregateType.Vector4 | AggregateType.U32),
|
||||||
|
IoVariable.SubgroupGtMask => (BuiltIn.SubgroupGtMask, AggregateType.Vector4 | AggregateType.U32),
|
||||||
|
IoVariable.SubgroupLaneId => (BuiltIn.SubgroupLocalInvocationId, AggregateType.U32),
|
||||||
|
IoVariable.SubgroupLeMask => (BuiltIn.SubgroupLeMask, AggregateType.Vector4 | AggregateType.U32),
|
||||||
|
IoVariable.SubgroupLtMask => (BuiltIn.SubgroupLtMask, AggregateType.Vector4 | AggregateType.U32),
|
||||||
|
IoVariable.TessellationCoord => (BuiltIn.TessCoord, AggregateType.Vector3 | AggregateType.FP32),
|
||||||
|
IoVariable.TessellationLevelInner => (BuiltIn.TessLevelInner, AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.TessellationLevelOuter => (BuiltIn.TessLevelOuter, AggregateType.Array | AggregateType.FP32),
|
||||||
|
IoVariable.ThreadId => (BuiltIn.LocalInvocationId, AggregateType.Vector3 | AggregateType.U32),
|
||||||
|
IoVariable.ThreadKill => (BuiltIn.HelperInvocation, AggregateType.Bool),
|
||||||
|
IoVariable.VertexId => (BuiltIn.VertexId, AggregateType.S32),
|
||||||
|
IoVariable.VertexIndex => (BuiltIn.VertexIndex, AggregateType.S32),
|
||||||
|
IoVariable.ViewportIndex => (BuiltIn.ViewportIndex, AggregateType.S32),
|
||||||
|
IoVariable.ViewportMask => (BuiltIn.ViewportMaskNV, AggregateType.Array | AggregateType.S32),
|
||||||
|
_ => (default, AggregateType.Invalid)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetSpirvBuiltInArrayLength(IoVariable ioVariable)
|
||||||
|
{
|
||||||
|
return ioVariable switch
|
||||||
|
{
|
||||||
|
IoVariable.ClipDistance => 8,
|
||||||
|
IoVariable.TessellationLevelInner => 2,
|
||||||
|
IoVariable.TessellationLevelOuter => 4,
|
||||||
|
IoVariable.ViewportMask => 1,
|
||||||
|
IoVariable.UserDefined => MaxAttributes,
|
||||||
|
_ => 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPerVertex(IoVariable ioVariable, ShaderStage stage, bool isOutput)
|
||||||
|
{
|
||||||
|
switch (ioVariable)
|
||||||
|
{
|
||||||
|
case IoVariable.Layer:
|
||||||
|
case IoVariable.ViewportIndex:
|
||||||
|
case IoVariable.PointSize:
|
||||||
|
case IoVariable.Position:
|
||||||
|
case IoVariable.UserDefined:
|
||||||
|
case IoVariable.ClipDistance:
|
||||||
|
case IoVariable.PointCoord:
|
||||||
|
case IoVariable.ViewportMask:
|
||||||
|
return !isOutput &&
|
||||||
|
(stage == ShaderStage.TessellationControl ||
|
||||||
|
stage == ShaderStage.TessellationEvaluation ||
|
||||||
|
stage == ShaderStage.Geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
|
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
|
||||||
|
|
||||||
var fragCoordPointer = context.Inputs[AttributeConsts.PositionX];
|
var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)];
|
||||||
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
||||||
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
||||||
|
|
||||||
|
@@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
if (config.Stage == ShaderStage.Fragment)
|
if (config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
if (context.Info.Inputs.Contains(AttributeConsts.Layer))
|
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)))
|
||||||
{
|
{
|
||||||
context.AddCapability(Capability.Geometry);
|
context.AddCapability(Capability.Geometry);
|
||||||
}
|
}
|
||||||
@@ -93,13 +93,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.AddCapability(Capability.DrawParameters);
|
context.AddCapability(Capability.DrawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
Declarations.DeclareAll(context, info);
|
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.ViewportMask)))
|
||||||
|
{
|
||||||
|
context.AddExtension("SPV_NV_viewport_array2");
|
||||||
|
context.AddCapability(Capability.ShaderViewportMaskNV);
|
||||||
|
}
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
||||||
{
|
{
|
||||||
Declarations.DeclareInvocationId(context);
|
info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.SubgroupLaneId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Declarations.DeclareAll(context, info);
|
||||||
|
|
||||||
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
|
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
|
||||||
{
|
{
|
||||||
var function = info.Functions[funcIndex];
|
var function = info.Functions[funcIndex];
|
||||||
@@ -203,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
// We invert the front face on Vulkan backend, so we need to do that here aswell.
|
// We invert the front face on Vulkan backend, so we need to do that here as well.
|
||||||
tessCw = !tessCw;
|
tessCw = !tessCw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
? ExecutionMode.OriginUpperLeft
|
? ExecutionMode.OriginUpperLeft
|
||||||
: ExecutionMode.OriginLowerLeft);
|
: ExecutionMode.OriginLowerLeft);
|
||||||
|
|
||||||
if (context.Outputs.ContainsKey(AttributeConsts.FragmentOutputDepth))
|
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.FragmentOutputDepth)))
|
||||||
{
|
{
|
||||||
context.AddExecutionMode(spvFunc, ExecutionMode.DepthReplacing);
|
context.AddExecutionMode(spvFunc, ExecutionMode.DepthReplacing);
|
||||||
}
|
}
|
||||||
@@ -389,21 +395,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var source = context.Get(dest.VarType, assignment.Source);
|
var source = context.Get(dest.VarType, assignment.Source);
|
||||||
context.Store(context.GetLocalPointer(dest), source);
|
context.Store(context.GetLocalPointer(dest), source);
|
||||||
}
|
}
|
||||||
else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch)
|
|
||||||
{
|
|
||||||
bool perPatch = dest.Type == OperandType.AttributePerPatch;
|
|
||||||
|
|
||||||
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch))
|
|
||||||
{
|
|
||||||
AggregateType elemType;
|
|
||||||
|
|
||||||
var elemPointer = perPatch
|
|
||||||
? context.GetAttributePerPatchElemPointer(dest.Value, true, out elemType)
|
|
||||||
: context.GetAttributeElemPointer(dest.Value, true, null, out elemType);
|
|
||||||
|
|
||||||
context.Store(elemPointer, context.Get(elemType, assignment.Source));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dest.Type == OperandType.Argument)
|
else if (dest.Type == OperandType.Argument)
|
||||||
{
|
{
|
||||||
var source = context.Get(dest.VarType, assignment.Source);
|
var source = context.Get(dest.VarType, assignment.Source);
|
||||||
|
@@ -295,10 +295,12 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
if (isStore)
|
if (isStore)
|
||||||
{
|
{
|
||||||
config.SetAllOutputUserAttributes();
|
config.SetAllOutputUserAttributes();
|
||||||
|
config.SetUsedFeature(FeatureFlags.OaIndexing);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
config.SetAllInputUserAttributes();
|
config.SetAllInputUserAttributes();
|
||||||
|
config.SetUsedFeature(FeatureFlags.IaIndexing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -340,7 +342,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isStore &&
|
if (!isStore &&
|
||||||
((attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) ||
|
(attr == AttributeConsts.FogCoord ||
|
||||||
|
(attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) ||
|
||||||
(attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)))
|
(attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)))
|
||||||
{
|
{
|
||||||
config.SetUsedFeature(FeatureFlags.FixedFuncAttr);
|
config.SetUsedFeature(FeatureFlags.FixedFuncAttr);
|
||||||
|
@@ -196,6 +196,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host's gather operation precision bits for biasing their coordinates. Zero means no bias.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Bits of gather operation precision to use for coordinate bias</returns>
|
||||||
|
int QueryHostGatherBiasPrecision()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host about whether to reduce precision to improve performance.
|
/// Queries host about whether to reduce precision to improve performance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -296,9 +305,9 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host support for writes to Layer from vertex or tessellation shader stages.
|
/// Queries host support for writes to the layer from vertex or tessellation shader stages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if writes to layer from vertex or tessellation are supported, false otherwise</returns>
|
/// <returns>True if writes to the layer from vertex or tessellation are supported, false otherwise</returns>
|
||||||
bool QueryHostSupportsLayerVertexTessellation()
|
bool QueryHostSupportsLayerVertexTessellation()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -341,10 +350,19 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host GPU shader viewport index output support.
|
/// Queries host support for writes to the viewport index from vertex or tessellation shader stages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the GPU and driver supports shader viewport index output, false otherwise</returns>
|
/// <returns>True if writes to the viewport index from vertex or tessellation are supported, false otherwise</returns>
|
||||||
bool QueryHostSupportsViewportIndex()
|
bool QueryHostSupportsViewportIndexVertexTessellation()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host GPU shader viewport mask output support.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the GPU and driver supports shader viewport mask output, false otherwise</returns>
|
||||||
|
bool QueryHostSupportsViewportMask()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
351
Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs
Normal file
351
Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
{
|
||||||
|
static class AttributeMap
|
||||||
|
{
|
||||||
|
private enum StagesMask : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Compute = 1 << (int)ShaderStage.Compute,
|
||||||
|
Vertex = 1 << (int)ShaderStage.Vertex,
|
||||||
|
TessellationControl = 1 << (int)ShaderStage.TessellationControl,
|
||||||
|
TessellationEvaluation = 1 << (int)ShaderStage.TessellationEvaluation,
|
||||||
|
Geometry = 1 << (int)ShaderStage.Geometry,
|
||||||
|
Fragment = 1 << (int)ShaderStage.Fragment,
|
||||||
|
|
||||||
|
Tessellation = TessellationControl | TessellationEvaluation,
|
||||||
|
VertexTessellationGeometry = Vertex | Tessellation | Geometry,
|
||||||
|
TessellationGeometryFragment = Tessellation | Geometry | Fragment,
|
||||||
|
AllGraphics = Vertex | Tessellation | Geometry | Fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct AttributeEntry
|
||||||
|
{
|
||||||
|
public int BaseOffset { get; }
|
||||||
|
public AggregateType Type { get; }
|
||||||
|
public IoVariable IoVariable { get; }
|
||||||
|
public StagesMask InputMask { get; }
|
||||||
|
public StagesMask OutputMask { get; }
|
||||||
|
|
||||||
|
public AttributeEntry(
|
||||||
|
int baseOffset,
|
||||||
|
AggregateType type,
|
||||||
|
IoVariable ioVariable,
|
||||||
|
StagesMask inputMask,
|
||||||
|
StagesMask outputMask)
|
||||||
|
{
|
||||||
|
BaseOffset = baseOffset;
|
||||||
|
Type = type;
|
||||||
|
IoVariable = ioVariable;
|
||||||
|
InputMask = inputMask;
|
||||||
|
OutputMask = outputMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributes;
|
||||||
|
private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributesPerPatch;
|
||||||
|
|
||||||
|
static AttributeMap()
|
||||||
|
{
|
||||||
|
_attributes = CreateMap();
|
||||||
|
_attributesPerPatch = CreatePerPatchMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyDictionary<int, AttributeEntry> CreateMap()
|
||||||
|
{
|
||||||
|
var map = new Dictionary<int, AttributeEntry>();
|
||||||
|
|
||||||
|
Add(map, 0x060, AggregateType.S32, IoVariable.PrimitiveId, StagesMask.TessellationGeometryFragment, StagesMask.Geometry);
|
||||||
|
Add(map, 0x064, AggregateType.S32, IoVariable.Layer, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x068, AggregateType.S32, IoVariable.ViewportIndex, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x06c, AggregateType.FP32, IoVariable.PointSize, StagesMask.None, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x070, AggregateType.Vector4 | AggregateType.FP32, IoVariable.Position, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x080, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.AllGraphics, StagesMask.VertexTessellationGeometry, 32);
|
||||||
|
Add(map, 0x280, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x290, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x2a0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x2b0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x2c0, AggregateType.Array | AggregateType.FP32, IoVariable.ClipDistance, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry, 8);
|
||||||
|
Add(map, 0x2e0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.PointCoord, StagesMask.Fragment, StagesMask.None);
|
||||||
|
Add(map, 0x2e8, AggregateType.FP32, IoVariable.FogCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x2f0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationCoord, StagesMask.TessellationEvaluation, StagesMask.None);
|
||||||
|
Add(map, 0x2f8, AggregateType.S32, IoVariable.InstanceId, StagesMask.Vertex, StagesMask.None);
|
||||||
|
Add(map, 0x2fc, AggregateType.S32, IoVariable.VertexId, StagesMask.Vertex, StagesMask.None);
|
||||||
|
Add(map, 0x300, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TextureCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x3a0, AggregateType.Array | AggregateType.S32, IoVariable.ViewportMask, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
|
||||||
|
Add(map, 0x3fc, AggregateType.Bool, IoVariable.FrontFacing, StagesMask.Fragment, StagesMask.None);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyDictionary<int, AttributeEntry> CreatePerPatchMap()
|
||||||
|
{
|
||||||
|
var map = new Dictionary<int, AttributeEntry>();
|
||||||
|
|
||||||
|
Add(map, 0x000, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TessellationLevelOuter, StagesMask.TessellationEvaluation, StagesMask.TessellationControl);
|
||||||
|
Add(map, 0x010, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationLevelInner, StagesMask.TessellationEvaluation, StagesMask.TessellationControl);
|
||||||
|
Add(map, 0x018, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.TessellationEvaluation, StagesMask.TessellationControl, 31, 0x200);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Add(
|
||||||
|
Dictionary<int, AttributeEntry> attributes,
|
||||||
|
int offset,
|
||||||
|
AggregateType type,
|
||||||
|
IoVariable ioVariable,
|
||||||
|
StagesMask inputMask,
|
||||||
|
StagesMask outputMask,
|
||||||
|
int count = 1,
|
||||||
|
int upperBound = 0x400)
|
||||||
|
{
|
||||||
|
int baseOffset = offset;
|
||||||
|
|
||||||
|
int elementsCount = GetElementCount(type);
|
||||||
|
|
||||||
|
for (int index = 0; index < count; index++)
|
||||||
|
{
|
||||||
|
for (int elementIndex = 0; elementIndex < elementsCount; elementIndex++)
|
||||||
|
{
|
||||||
|
attributes.Add(offset, new AttributeEntry(baseOffset, type, ioVariable, inputMask, outputMask));
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
if (offset >= upperBound)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Operand GenerateAttributeLoad(EmitterContext context, Operand primVertex, int offset, bool isOutput, bool isPerPatch)
|
||||||
|
{
|
||||||
|
if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry))
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid.");
|
||||||
|
return Const(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
StagesMask validUseMask = isOutput ? entry.OutputMask : entry.InputMask;
|
||||||
|
|
||||||
|
if (((StagesMask)(1 << (int)context.Config.Stage) & validUseMask) == StagesMask.None)
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}.");
|
||||||
|
return Const(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable))
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}.");
|
||||||
|
return Const(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasInvocationId(context.Config.Stage, isOutput) && !isPerPatch)
|
||||||
|
{
|
||||||
|
primVertex = context.Load(StorageKind.Input, IoVariable.InvocationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int innerOffset = offset - entry.BaseOffset;
|
||||||
|
int innerIndex = innerOffset / 4;
|
||||||
|
|
||||||
|
StorageKind storageKind = isPerPatch
|
||||||
|
? (isOutput ? StorageKind.OutputPerPatch : StorageKind.InputPerPatch)
|
||||||
|
: (isOutput ? StorageKind.Output : StorageKind.Input);
|
||||||
|
IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry);
|
||||||
|
AggregateType type = GetType(context.Config, isOutput, innerIndex, in entry);
|
||||||
|
int elementCount = GetElementCount(type);
|
||||||
|
|
||||||
|
bool isArray = type.HasFlag(AggregateType.Array);
|
||||||
|
bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput);
|
||||||
|
|
||||||
|
bool hasElementIndex = elementCount > 1;
|
||||||
|
|
||||||
|
if (hasArrayIndex && hasElementIndex)
|
||||||
|
{
|
||||||
|
int arrayIndex = innerIndex / elementCount;
|
||||||
|
int elementIndex = innerIndex - (arrayIndex * elementCount);
|
||||||
|
|
||||||
|
return primVertex == null || isArray
|
||||||
|
? context.Load(storageKind, ioVariable, primVertex, Const(arrayIndex), Const(elementIndex))
|
||||||
|
: context.Load(storageKind, ioVariable, Const(arrayIndex), primVertex, Const(elementIndex));
|
||||||
|
}
|
||||||
|
else if (hasArrayIndex || hasElementIndex)
|
||||||
|
{
|
||||||
|
return primVertex == null || isArray || !hasArrayIndex
|
||||||
|
? context.Load(storageKind, ioVariable, primVertex, Const(innerIndex))
|
||||||
|
: context.Load(storageKind, ioVariable, Const(innerIndex), primVertex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return context.Load(storageKind, ioVariable, primVertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GenerateAttributeStore(EmitterContext context, int offset, bool isPerPatch, Operand value)
|
||||||
|
{
|
||||||
|
if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry))
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((StagesMask)(1 << (int)context.Config.Stage) & entry.OutputMask) == StagesMask.None)
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable))
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand invocationId = null;
|
||||||
|
|
||||||
|
if (HasInvocationId(context.Config.Stage, isOutput: true) && !isPerPatch)
|
||||||
|
{
|
||||||
|
invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int innerOffset = offset - entry.BaseOffset;
|
||||||
|
int innerIndex = innerOffset / 4;
|
||||||
|
|
||||||
|
StorageKind storageKind = isPerPatch ? StorageKind.OutputPerPatch : StorageKind.Output;
|
||||||
|
IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry);
|
||||||
|
AggregateType type = GetType(context.Config, isOutput: true, innerIndex, in entry);
|
||||||
|
int elementCount = GetElementCount(type);
|
||||||
|
|
||||||
|
bool isArray = type.HasFlag(AggregateType.Array);
|
||||||
|
bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput: true);
|
||||||
|
|
||||||
|
bool hasElementIndex = elementCount > 1;
|
||||||
|
|
||||||
|
if (hasArrayIndex && hasElementIndex)
|
||||||
|
{
|
||||||
|
int arrayIndex = innerIndex / elementCount;
|
||||||
|
int elementIndex = innerIndex - (arrayIndex * elementCount);
|
||||||
|
|
||||||
|
if (invocationId == null || isArray)
|
||||||
|
{
|
||||||
|
context.Store(storageKind, ioVariable, invocationId, Const(arrayIndex), Const(elementIndex), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Store(storageKind, ioVariable, Const(arrayIndex), invocationId, Const(elementIndex), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (hasArrayIndex || hasElementIndex)
|
||||||
|
{
|
||||||
|
if (invocationId == null || isArray || !hasArrayIndex)
|
||||||
|
{
|
||||||
|
context.Store(storageKind, ioVariable, invocationId, Const(innerIndex), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Store(storageKind, ioVariable, Const(innerIndex), invocationId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Store(storageKind, ioVariable, invocationId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSupportedByHost(IGpuAccessor gpuAccessor, ShaderStage stage, IoVariable ioVariable)
|
||||||
|
{
|
||||||
|
if (ioVariable == IoVariable.ViewportIndex && stage != ShaderStage.Geometry && stage != ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
return gpuAccessor.QueryHostSupportsViewportIndexVertexTessellation();
|
||||||
|
}
|
||||||
|
else if (ioVariable == IoVariable.ViewportMask)
|
||||||
|
{
|
||||||
|
return gpuAccessor.QueryHostSupportsViewportMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IoVariable GetIoVariable(ShaderConfig config, int offset, out int location)
|
||||||
|
{
|
||||||
|
location = 0;
|
||||||
|
|
||||||
|
if (!_attributes.TryGetValue(offset, out AttributeEntry entry))
|
||||||
|
{
|
||||||
|
return IoVariable.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((StagesMask)(1 << (int)config.Stage) & entry.OutputMask) == StagesMask.None)
|
||||||
|
{
|
||||||
|
return IoVariable.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.HasPerLocationInputOrOutput(entry.IoVariable, isOutput: true))
|
||||||
|
{
|
||||||
|
location = (offset - entry.BaseOffset) / 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetIoVariable(config.Stage, in entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IoVariable GetIoVariable(ShaderStage stage, in AttributeEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.IoVariable == IoVariable.Position && stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
return IoVariable.FragmentCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.IoVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AggregateType GetType(ShaderConfig config, bool isOutput, int innerIndex, in AttributeEntry entry)
|
||||||
|
{
|
||||||
|
AggregateType type = entry.Type;
|
||||||
|
|
||||||
|
if (entry.IoVariable == IoVariable.UserDefined)
|
||||||
|
{
|
||||||
|
type = config.GetUserDefinedType(innerIndex / 4, isOutput);
|
||||||
|
}
|
||||||
|
else if (entry.IoVariable == IoVariable.FragmentOutputColor)
|
||||||
|
{
|
||||||
|
type = config.GetFragmentOutputColorType(innerIndex / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasPrimitiveVertex(ShaderStage stage, bool isOutput)
|
||||||
|
{
|
||||||
|
if (isOutput)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage == ShaderStage.TessellationControl ||
|
||||||
|
stage == ShaderStage.TessellationEvaluation ||
|
||||||
|
stage == ShaderStage.Geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasInvocationId(ShaderStage stage, bool isOutput)
|
||||||
|
{
|
||||||
|
return isOutput && stage == ShaderStage.TessellationControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetElementCount(AggregateType type)
|
||||||
|
{
|
||||||
|
return (type & AggregateType.ElementCountMask) switch
|
||||||
|
{
|
||||||
|
AggregateType.Vector2 => 2,
|
||||||
|
AggregateType.Vector3 => 3,
|
||||||
|
AggregateType.Vector4 => 4,
|
||||||
|
_ => 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -20,7 +20,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
{
|
{
|
||||||
InstAld op = context.GetOp<InstAld>();
|
InstAld op = context.GetOp<InstAld>();
|
||||||
|
|
||||||
Operand primVertex = context.Copy(GetSrcReg(context, op.SrcB));
|
// Some of those attributes are per invocation,
|
||||||
|
// so we should ignore any primitive vertex indexing for those.
|
||||||
|
bool hasPrimitiveVertex = AttributeMap.HasPrimitiveVertex(context.Config.Stage, op.O) && !op.P;
|
||||||
|
|
||||||
|
if (!op.Phys)
|
||||||
|
{
|
||||||
|
hasPrimitiveVertex &= HasPrimitiveVertex(op.Imm11);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand primVertex = hasPrimitiveVertex ? context.Copy(GetSrcReg(context, op.SrcB)) : null;
|
||||||
|
|
||||||
for (int index = 0; index < (int)op.AlSize + 1; index++)
|
for (int index = 0; index < (int)op.AlSize + 1; index++)
|
||||||
{
|
{
|
||||||
@@ -33,12 +42,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (op.Phys)
|
if (op.Phys)
|
||||||
{
|
{
|
||||||
Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
|
Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
|
||||||
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
|
Operand vecIndex = context.ShiftRightU32(offset, Const(4));
|
||||||
|
Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
|
||||||
|
|
||||||
context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex));
|
StorageKind storageKind = op.O ? StorageKind.Output : StorageKind.Input;
|
||||||
|
|
||||||
context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
|
context.Copy(Register(rd), context.Load(storageKind, IoVariable.UserDefined, primVertex, vecIndex, elemIndex));
|
||||||
}
|
}
|
||||||
else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P)
|
else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P)
|
||||||
{
|
{
|
||||||
@@ -46,14 +56,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
context.FlagAttributeRead(offset);
|
context.FlagAttributeRead(offset);
|
||||||
|
|
||||||
if (op.O && CanLoadOutput(offset))
|
bool isOutput = op.O && CanLoadOutput(offset);
|
||||||
|
|
||||||
|
if (!op.P && !isOutput && TryConvertIdToIndexForVulkan(context, offset, out Operand value))
|
||||||
{
|
{
|
||||||
offset |= AttributeConsts.LoadOutputMask;
|
context.Copy(Register(rd), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, op.P));
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand src = op.P ? AttributePerPatch(offset) : CreateInputAttribute(context, offset);
|
|
||||||
|
|
||||||
context.Copy(Register(rd), src);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -61,14 +73,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
context.FlagAttributeRead(offset);
|
context.FlagAttributeRead(offset);
|
||||||
|
|
||||||
if (op.O && CanLoadOutput(offset))
|
bool isOutput = op.O && CanLoadOutput(offset);
|
||||||
{
|
|
||||||
offset |= AttributeConsts.LoadOutputMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand src = Const(offset);
|
context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, false));
|
||||||
|
|
||||||
context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,12 +95,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (op.Phys)
|
if (op.Phys)
|
||||||
{
|
{
|
||||||
Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
|
Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
|
||||||
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
|
Operand vecIndex = context.ShiftRightU32(offset, Const(4));
|
||||||
|
Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
|
||||||
|
Operand invocationId = AttributeMap.HasInvocationId(context.Config.Stage, isOutput: true)
|
||||||
|
? context.Load(StorageKind.Input, IoVariable.InvocationId)
|
||||||
|
: null;
|
||||||
|
|
||||||
context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd));
|
context.Store(StorageKind.Output, IoVariable.UserDefined, invocationId, vecIndex, elemIndex, Register(rd));
|
||||||
|
|
||||||
context.Config.SetUsedFeature(FeatureFlags.OaIndexing);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -110,9 +119,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
context.FlagAttributeWritten(offset);
|
context.FlagAttributeWritten(offset);
|
||||||
|
|
||||||
Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset);
|
AttributeMap.GenerateAttributeStore(context, offset, op.P, Register(rd));
|
||||||
|
|
||||||
context.Copy(dest, Register(rd));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,13 +136,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (op.Idx)
|
if (op.Idx)
|
||||||
{
|
{
|
||||||
Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
|
Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
|
||||||
Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
|
Operand vecIndex = context.ShiftRightU32(offset, Const(4));
|
||||||
|
Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
|
||||||
|
|
||||||
res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0));
|
res = context.Load(StorageKind.Input, IoVariable.UserDefined, null, vecIndex, elemIndex);
|
||||||
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
|
res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3)));
|
||||||
|
|
||||||
context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -147,9 +153,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
|
if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
|
||||||
{
|
{
|
||||||
res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
|
res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (op.Imm10 == AttributeConsts.PositionX || op.Imm10 == AttributeConsts.PositionY)
|
||||||
|
{
|
||||||
|
// FragCoord X/Y must be divided by the render target scale, if resolution scaling is active,
|
||||||
|
// because the shader code is not expecting scaled values.
|
||||||
|
res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0)));
|
||||||
|
}
|
||||||
|
else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
|
||||||
|
{
|
||||||
|
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
|
||||||
|
// This weird trick makes it behave.
|
||||||
|
res = context.ICompareLess(context.INegate(context.IConvertS32ToFP32(res)), Const(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.IpaOp == IpaOp.Multiply && !isFixedFunc)
|
if (op.IpaOp == IpaOp.Multiply && !isFixedFunc)
|
||||||
@@ -216,17 +234,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (tempXLocal != null)
|
if (tempXLocal != null)
|
||||||
{
|
{
|
||||||
context.Copy(Attribute(AttributeConsts.PositionX), tempXLocal);
|
context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(0)), tempXLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempYLocal != null)
|
if (tempYLocal != null)
|
||||||
{
|
{
|
||||||
context.Copy(Attribute(AttributeConsts.PositionY), tempYLocal);
|
context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(1)), tempYLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempZLocal != null)
|
if (tempZLocal != null)
|
||||||
{
|
{
|
||||||
context.Copy(Attribute(AttributeConsts.PositionZ), tempZLocal);
|
context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(2)), tempZLocal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -241,6 +259,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasPrimitiveVertex(int attr)
|
||||||
|
{
|
||||||
|
return attr != AttributeConsts.PrimitiveId &&
|
||||||
|
attr != AttributeConsts.TessCoordX &&
|
||||||
|
attr != AttributeConsts.TessCoordY;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool CanLoadOutput(int attr)
|
private static bool CanLoadOutput(int attr)
|
||||||
{
|
{
|
||||||
return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY;
|
return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY;
|
||||||
@@ -252,13 +277,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
{
|
{
|
||||||
// TODO: If two sided rendering is enabled, then this should return
|
// TODO: If two sided rendering is enabled, then this should return
|
||||||
// FrontColor if the fragment is front facing, and back color otherwise.
|
// FrontColor if the fragment is front facing, and back color otherwise.
|
||||||
int index = (attr - AttributeConsts.FrontColorDiffuseR) >> 4;
|
selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
|
||||||
int userAttrIndex = context.Config.GetFreeUserAttribute(isOutput: false, index);
|
return true;
|
||||||
Operand frontAttr = Attribute(AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf));
|
}
|
||||||
|
else if (attr == AttributeConsts.FogCoord)
|
||||||
context.Config.SetInputUserAttributeFixedFunc(userAttrIndex);
|
{
|
||||||
|
// TODO: We likely need to emulate the fixed-function functionality for FogCoord here.
|
||||||
selectedAttr = frontAttr;
|
selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
||||||
@@ -268,14 +293,19 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
}
|
}
|
||||||
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
||||||
{
|
{
|
||||||
selectedAttr = Attribute(FixedFuncToUserAttribute(context.Config, attr, AttributeConsts.TexCoordBase, 4, isOutput: false));
|
selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedAttr = Attribute(attr);
|
selectedAttr = GenerateIpaLoad(context, attr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateIpaLoad(EmitterContext context, int offset)
|
||||||
|
{
|
||||||
|
return AttributeMap.GenerateAttributeLoad(context, null, offset, isOutput: false, isPerPatch: false);
|
||||||
|
}
|
||||||
|
|
||||||
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
|
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
|
||||||
{
|
{
|
||||||
bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
|
bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
|
||||||
@@ -286,13 +316,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput);
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput);
|
||||||
config.SetLayerOutputAttribute(attr);
|
config.SetLayerOutputAttribute(attr);
|
||||||
}
|
}
|
||||||
|
else if (attr == AttributeConsts.FogCoord)
|
||||||
|
{
|
||||||
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FogCoord, fixedStartAttr, isOutput);
|
||||||
|
}
|
||||||
else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
||||||
{
|
{
|
||||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr, isOutput);
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr + 1, isOutput);
|
||||||
}
|
}
|
||||||
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
||||||
{
|
{
|
||||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 4, isOutput);
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 5, isOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attr;
|
return attr;
|
||||||
@@ -301,11 +335,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput)
|
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput)
|
||||||
{
|
{
|
||||||
int index = (attr - baseAttr) >> 4;
|
int index = (attr - baseAttr) >> 4;
|
||||||
int userAttrIndex = config.GetFreeUserAttribute(isOutput, index);
|
int userAttrIndex = config.GetFreeUserAttribute(isOutput, baseIndex + index);
|
||||||
|
|
||||||
if ((uint)userAttrIndex < Constants.MaxAttributes)
|
if ((uint)userAttrIndex < Constants.MaxAttributes)
|
||||||
{
|
{
|
||||||
userAttrIndex += baseIndex;
|
|
||||||
attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf);
|
attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf);
|
||||||
|
|
||||||
if (isOutput)
|
if (isOutput)
|
||||||
@@ -317,25 +350,34 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
config.SetInputUserAttributeFixedFunc(userAttrIndex);
|
config.SetInputUserAttributeFixedFunc(userAttrIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.GpuAccessor.Log($"No enough user attributes for fixed attribute offset 0x{attr:X}.");
|
||||||
|
}
|
||||||
|
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Operand CreateInputAttribute(EmitterContext context, int attr)
|
private static bool TryConvertIdToIndexForVulkan(EmitterContext context, int attr, out Operand value)
|
||||||
{
|
{
|
||||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
if (attr == AttributeConsts.InstanceId)
|
if (attr == AttributeConsts.InstanceId)
|
||||||
{
|
{
|
||||||
return context.ISubtract(Attribute(AttributeConsts.InstanceIndex), Attribute(AttributeConsts.BaseInstance));
|
value = context.ISubtract(
|
||||||
|
context.Load(StorageKind.Input, IoVariable.InstanceIndex),
|
||||||
|
context.Load(StorageKind.Input, IoVariable.BaseInstance));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (attr == AttributeConsts.VertexId)
|
else if (attr == AttributeConsts.VertexId)
|
||||||
{
|
{
|
||||||
return Attribute(AttributeConsts.VertexIndex);
|
value = context.Load(StorageKind.Input, IoVariable.VertexIndex);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Attribute(attr);
|
value = null;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
Operand value = GetSrcReg(context, op.SrcB);
|
Operand value = GetSrcReg(context, op.SrcB);
|
||||||
|
|
||||||
Operand res = EmitAtomicOp(context, Instruction.MrGlobal, op.Op, op.Size, addrLow, addrHigh, value);
|
Operand res = EmitAtomicOp(context, StorageKind.GlobalMemory, op.Op, op.Size, addrLow, addrHigh, value);
|
||||||
|
|
||||||
context.Copy(GetDest(op.Dest), res);
|
context.Copy(GetDest(op.Dest), res);
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
_ => AtomSize.U32
|
_ => AtomSize.U32
|
||||||
};
|
};
|
||||||
|
|
||||||
Operand res = EmitAtomicOp(context, Instruction.MrShared, op.AtomOp, size, offset, Const(0), value);
|
Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, offset, Const(0), value);
|
||||||
|
|
||||||
context.Copy(GetDest(op.Dest), res);
|
context.Copy(GetDest(op.Dest), res);
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(op.SrcA, RegisterType.Gpr), op.E, op.Imm20);
|
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(op.SrcA, RegisterType.Gpr), op.E, op.Imm20);
|
||||||
|
|
||||||
EmitAtomicOp(context, Instruction.MrGlobal, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB));
|
EmitAtomicOp(context, StorageKind.GlobalMemory, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Stg(EmitterContext context)
|
public static void Stg(EmitterContext context)
|
||||||
@@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
private static Operand EmitAtomicOp(
|
private static Operand EmitAtomicOp(
|
||||||
EmitterContext context,
|
EmitterContext context,
|
||||||
Instruction mr,
|
StorageKind storageKind,
|
||||||
AtomOp op,
|
AtomOp op,
|
||||||
AtomSize type,
|
AtomSize type,
|
||||||
Operand addrLow,
|
Operand addrLow,
|
||||||
@@ -170,7 +170,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
case AtomOp.Add:
|
case AtomOp.Add:
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicAdd(mr, addrLow, addrHigh, value);
|
res = context.AtomicAdd(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
case AtomOp.And:
|
case AtomOp.And:
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicAnd(mr, addrLow, addrHigh, value);
|
res = context.AtomicAnd(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
case AtomOp.Xor:
|
case AtomOp.Xor:
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicXor(mr, addrLow, addrHigh, value);
|
res = context.AtomicXor(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
case AtomOp.Or:
|
case AtomOp.Or:
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicOr(mr, addrLow, addrHigh, value);
|
res = context.AtomicOr(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -210,11 +210,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
case AtomOp.Max:
|
case AtomOp.Max:
|
||||||
if (type == AtomSize.S32)
|
if (type == AtomSize.S32)
|
||||||
{
|
{
|
||||||
res = context.AtomicMaxS32(mr, addrLow, addrHigh, value);
|
res = context.AtomicMaxS32(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else if (type == AtomSize.U32)
|
else if (type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicMaxU32(mr, addrLow, addrHigh, value);
|
res = context.AtomicMaxU32(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -224,11 +224,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
case AtomOp.Min:
|
case AtomOp.Min:
|
||||||
if (type == AtomSize.S32)
|
if (type == AtomSize.S32)
|
||||||
{
|
{
|
||||||
res = context.AtomicMinS32(mr, addrLow, addrHigh, value);
|
res = context.AtomicMinS32(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else if (type == AtomSize.U32)
|
else if (type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicMinU32(mr, addrLow, addrHigh, value);
|
res = context.AtomicMinU32(storageKind, addrLow, addrHigh, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -76,11 +76,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
switch (op.SReg)
|
switch (op.SReg)
|
||||||
{
|
{
|
||||||
case SReg.LaneId:
|
case SReg.LaneId:
|
||||||
src = Attribute(AttributeConsts.LaneId);
|
src = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.InvocationId:
|
case SReg.InvocationId:
|
||||||
src = Attribute(AttributeConsts.InvocationId);
|
src = context.Load(StorageKind.Input, IoVariable.InvocationId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.YDirection:
|
case SReg.YDirection:
|
||||||
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.ThreadKill:
|
case SReg.ThreadKill:
|
||||||
src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0);
|
src = context.Config.Stage == ShaderStage.Fragment ? context.Load(StorageKind.Input, IoVariable.ThreadKill) : Const(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.InvocationInfo:
|
case SReg.InvocationInfo:
|
||||||
@@ -101,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
if (context.Config.Stage == ShaderStage.TessellationControl ||
|
if (context.Config.Stage == ShaderStage.TessellationControl ||
|
||||||
context.Config.Stage == ShaderStage.TessellationEvaluation)
|
context.Config.Stage == ShaderStage.TessellationEvaluation)
|
||||||
{
|
{
|
||||||
src = context.ShiftLeft(Attribute(AttributeConsts.PatchVerticesIn), Const(16));
|
src = context.ShiftLeft(context.Load(StorageKind.Input, IoVariable.PatchVertices), Const(16));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -115,9 +115,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.TId:
|
case SReg.TId:
|
||||||
Operand tidX = Attribute(AttributeConsts.ThreadIdX);
|
Operand tidX = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(0));
|
||||||
Operand tidY = Attribute(AttributeConsts.ThreadIdY);
|
Operand tidY = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(1));
|
||||||
Operand tidZ = Attribute(AttributeConsts.ThreadIdZ);
|
Operand tidZ = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(2));
|
||||||
|
|
||||||
tidY = context.ShiftLeft(tidY, Const(16));
|
tidY = context.ShiftLeft(tidY, Const(16));
|
||||||
tidZ = context.ShiftLeft(tidZ, Const(26));
|
tidZ = context.ShiftLeft(tidZ, Const(26));
|
||||||
@@ -126,39 +126,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.TIdX:
|
case SReg.TIdX:
|
||||||
src = Attribute(AttributeConsts.ThreadIdX);
|
src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(0));
|
||||||
break;
|
break;
|
||||||
case SReg.TIdY:
|
case SReg.TIdY:
|
||||||
src = Attribute(AttributeConsts.ThreadIdY);
|
src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(1));
|
||||||
break;
|
break;
|
||||||
case SReg.TIdZ:
|
case SReg.TIdZ:
|
||||||
src = Attribute(AttributeConsts.ThreadIdZ);
|
src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(2));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.CtaIdX:
|
case SReg.CtaIdX:
|
||||||
src = Attribute(AttributeConsts.CtaIdX);
|
src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(0));
|
||||||
break;
|
break;
|
||||||
case SReg.CtaIdY:
|
case SReg.CtaIdY:
|
||||||
src = Attribute(AttributeConsts.CtaIdY);
|
src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(1));
|
||||||
break;
|
break;
|
||||||
case SReg.CtaIdZ:
|
case SReg.CtaIdZ:
|
||||||
src = Attribute(AttributeConsts.CtaIdZ);
|
src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(2));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.EqMask:
|
case SReg.EqMask:
|
||||||
src = Attribute(AttributeConsts.EqMask);
|
src = context.Load(StorageKind.Input, IoVariable.SubgroupEqMask, null, Const(0));
|
||||||
break;
|
break;
|
||||||
case SReg.LtMask:
|
case SReg.LtMask:
|
||||||
src = Attribute(AttributeConsts.LtMask);
|
src = context.Load(StorageKind.Input, IoVariable.SubgroupLtMask, null, Const(0));
|
||||||
break;
|
break;
|
||||||
case SReg.LeMask:
|
case SReg.LeMask:
|
||||||
src = Attribute(AttributeConsts.LeMask);
|
src = context.Load(StorageKind.Input, IoVariable.SubgroupLeMask, null, Const(0));
|
||||||
break;
|
break;
|
||||||
case SReg.GtMask:
|
case SReg.GtMask:
|
||||||
src = Attribute(AttributeConsts.GtMask);
|
src = context.Load(StorageKind.Input, IoVariable.SubgroupGtMask, null, Const(0));
|
||||||
break;
|
break;
|
||||||
case SReg.GeMask:
|
case SReg.GeMask:
|
||||||
src = Attribute(AttributeConsts.GeMask);
|
src = context.Load(StorageKind.Input, IoVariable.SubgroupGeMask, null, Const(0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
ImageStore,
|
ImageStore,
|
||||||
ImageAtomic,
|
ImageAtomic,
|
||||||
IsNan,
|
IsNan,
|
||||||
LoadAttribute,
|
Load,
|
||||||
LoadConstant,
|
LoadConstant,
|
||||||
LoadGlobal,
|
LoadGlobal,
|
||||||
LoadLocal,
|
LoadLocal,
|
||||||
@@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
ShuffleXor,
|
ShuffleXor,
|
||||||
Sine,
|
Sine,
|
||||||
SquareRoot,
|
SquareRoot,
|
||||||
StoreAttribute,
|
Store,
|
||||||
StoreGlobal,
|
StoreGlobal,
|
||||||
StoreGlobal16,
|
StoreGlobal16,
|
||||||
StoreGlobal8,
|
StoreGlobal8,
|
||||||
@@ -144,13 +144,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
FP32 = 1 << 16,
|
FP32 = 1 << 16,
|
||||||
FP64 = 1 << 17,
|
FP64 = 1 << 17,
|
||||||
|
|
||||||
MrShift = 18,
|
|
||||||
|
|
||||||
MrGlobal = 0 << MrShift,
|
|
||||||
MrShared = 1 << MrShift,
|
|
||||||
MrStorage = 2 << MrShift,
|
|
||||||
MrMask = 3 << MrShift,
|
|
||||||
|
|
||||||
Mask = 0xffff
|
Mask = 0xffff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,51 @@
|
|||||||
|
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
{
|
||||||
|
enum IoVariable
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
|
||||||
|
BackColorDiffuse,
|
||||||
|
BackColorSpecular,
|
||||||
|
BaseInstance,
|
||||||
|
BaseVertex,
|
||||||
|
ClipDistance,
|
||||||
|
CtaId,
|
||||||
|
DrawIndex,
|
||||||
|
FogCoord,
|
||||||
|
FragmentCoord,
|
||||||
|
FragmentOutputColor,
|
||||||
|
FragmentOutputDepth,
|
||||||
|
FragmentOutputIsBgra, // TODO: Remove and use constant buffer access.
|
||||||
|
FrontColorDiffuse,
|
||||||
|
FrontColorSpecular,
|
||||||
|
FrontFacing,
|
||||||
|
InstanceId,
|
||||||
|
InstanceIndex,
|
||||||
|
InvocationId,
|
||||||
|
Layer,
|
||||||
|
PatchVertices,
|
||||||
|
PointCoord,
|
||||||
|
PointSize,
|
||||||
|
Position,
|
||||||
|
PrimitiveId,
|
||||||
|
SubgroupEqMask,
|
||||||
|
SubgroupGeMask,
|
||||||
|
SubgroupGtMask,
|
||||||
|
SubgroupLaneId,
|
||||||
|
SubgroupLeMask,
|
||||||
|
SubgroupLtMask,
|
||||||
|
SupportBlockViewInverse, // TODO: Remove and use constant buffer access.
|
||||||
|
SupportBlockRenderScale, // TODO: Remove and use constant buffer access.
|
||||||
|
TessellationCoord,
|
||||||
|
TessellationLevelInner,
|
||||||
|
TessellationLevelOuter,
|
||||||
|
TextureCoord,
|
||||||
|
ThreadId,
|
||||||
|
ThreadKill,
|
||||||
|
UserDefined,
|
||||||
|
VertexId,
|
||||||
|
VertexIndex,
|
||||||
|
ViewportIndex,
|
||||||
|
ViewportMask
|
||||||
|
}
|
||||||
|
}
|
@@ -10,16 +10,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
return new Operand(OperandType.Argument, value);
|
return new Operand(OperandType.Argument, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand Attribute(int value)
|
|
||||||
{
|
|
||||||
return new Operand(OperandType.Attribute, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand AttributePerPatch(int value)
|
|
||||||
{
|
|
||||||
return new Operand(OperandType.AttributePerPatch, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Cbuf(int slot, int offset)
|
public static Operand Cbuf(int slot, int offset)
|
||||||
{
|
{
|
||||||
return new Operand(slot, offset);
|
return new Operand(slot, offset);
|
||||||
|
@@ -3,8 +3,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
enum OperandType
|
enum OperandType
|
||||||
{
|
{
|
||||||
Argument,
|
Argument,
|
||||||
Attribute,
|
|
||||||
AttributePerPatch,
|
|
||||||
Constant,
|
Constant,
|
||||||
ConstantBuffer,
|
ConstantBuffer,
|
||||||
Label,
|
Label,
|
||||||
@@ -12,12 +10,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
Register,
|
Register,
|
||||||
Undefined
|
Undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
static class OperandTypeExtensions
|
|
||||||
{
|
|
||||||
public static bool IsAttribute(this OperandType type)
|
|
||||||
{
|
|
||||||
return type == OperandType.Attribute || type == OperandType.AttributePerPatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
class Operation : INode
|
class Operation : INode
|
||||||
{
|
{
|
||||||
public Instruction Inst { get; private set; }
|
public Instruction Inst { get; private set; }
|
||||||
|
public StorageKind StorageKind { get; }
|
||||||
|
|
||||||
private Operand[] _dests;
|
private Operand[] _dests;
|
||||||
|
|
||||||
@@ -99,6 +100,23 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Operation(Instruction inst, StorageKind storageKind, Operand dest, params Operand[] sources) : this(sources)
|
||||||
|
{
|
||||||
|
Inst = inst;
|
||||||
|
StorageKind = storageKind;
|
||||||
|
|
||||||
|
if (dest != null)
|
||||||
|
{
|
||||||
|
dest.AsgOp = this;
|
||||||
|
|
||||||
|
_dests = new[] { dest };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dests = Array.Empty<Operand>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Operation(Instruction inst, int index, Operand dest, params Operand[] sources) : this(inst, dest, sources)
|
public Operation(Instruction inst, int index, Operand dest, params Operand[] sources) : this(inst, dest, sources)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
|
@@ -0,0 +1,39 @@
|
|||||||
|
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
{
|
||||||
|
enum StorageKind
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Input,
|
||||||
|
InputPerPatch,
|
||||||
|
Output,
|
||||||
|
OutputPerPatch,
|
||||||
|
ConstantBuffer,
|
||||||
|
StorageBuffer,
|
||||||
|
LocalMemory,
|
||||||
|
SharedMemory,
|
||||||
|
GlobalMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
static class StorageKindExtensions
|
||||||
|
{
|
||||||
|
public static bool IsInputOrOutput(this StorageKind storageKind)
|
||||||
|
{
|
||||||
|
return storageKind == StorageKind.Input ||
|
||||||
|
storageKind == StorageKind.InputPerPatch ||
|
||||||
|
storageKind == StorageKind.Output ||
|
||||||
|
storageKind == StorageKind.OutputPerPatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsOutput(this StorageKind storageKind)
|
||||||
|
{
|
||||||
|
return storageKind == StorageKind.Output ||
|
||||||
|
storageKind == StorageKind.OutputPerPatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPerPatch(this StorageKind storageKind)
|
||||||
|
{
|
||||||
|
return storageKind == StorageKind.InputPerPatch ||
|
||||||
|
storageKind == StorageKind.OutputPerPatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
class AstOperation : AstNode
|
class AstOperation : AstNode
|
||||||
{
|
{
|
||||||
public Instruction Inst { get; }
|
public Instruction Inst { get; }
|
||||||
|
public StorageKind StorageKind { get; }
|
||||||
|
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
|
|
||||||
@@ -16,9 +17,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
public int SourcesCount => _sources.Length;
|
public int SourcesCount => _sources.Length;
|
||||||
|
|
||||||
public AstOperation(Instruction inst, IAstNode[] sources, int sourcesCount)
|
public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount)
|
||||||
{
|
{
|
||||||
Inst = inst;
|
Inst = inst;
|
||||||
|
StorageKind = storageKind;
|
||||||
_sources = sources;
|
_sources = sources;
|
||||||
|
|
||||||
for (int index = 0; index < sources.Length; index++)
|
for (int index = 0; index < sources.Length; index++)
|
||||||
@@ -36,12 +38,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Index = 0;
|
Index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstOperation(Instruction inst, int index, IAstNode[] sources, int sourcesCount) : this(inst, sources, sourcesCount)
|
public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, sources, sources.Length)
|
public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
int cbufSlot,
|
int cbufSlot,
|
||||||
int handle,
|
int handle,
|
||||||
int index,
|
int index,
|
||||||
params IAstNode[] sources) : base(inst, index, sources, sources.Length)
|
params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
Format = format;
|
Format = format;
|
||||||
|
@@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.ImageStore, AggregateType.Void);
|
Add(Instruction.ImageStore, AggregateType.Void);
|
||||||
Add(Instruction.ImageAtomic, AggregateType.S32);
|
Add(Instruction.ImageAtomic, AggregateType.S32);
|
||||||
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
||||||
Add(Instruction.LoadAttribute, AggregateType.FP32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.Load, AggregateType.FP32);
|
||||||
Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
||||||
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
|
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
|
||||||
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.StoreAttribute, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.FP32);
|
Add(Instruction.Store, AggregateType.Void);
|
||||||
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
|
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
|
||||||
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||||
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||||
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
return AggregateType.FP32;
|
return AggregateType.FP32;
|
||||||
}
|
}
|
||||||
else if (inst == Instruction.Call)
|
else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store)
|
||||||
{
|
{
|
||||||
return AggregateType.S32;
|
return AggregateType.S32;
|
||||||
}
|
}
|
||||||
|
44
Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs
Normal file
44
Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
{
|
||||||
|
readonly struct IoDefinition : IEquatable<IoDefinition>
|
||||||
|
{
|
||||||
|
public StorageKind StorageKind { get; }
|
||||||
|
public IoVariable IoVariable { get; }
|
||||||
|
public int Location { get; }
|
||||||
|
public int Component { get; }
|
||||||
|
|
||||||
|
public IoDefinition(StorageKind storageKind, IoVariable ioVariable, int location = 0, int component = 0)
|
||||||
|
{
|
||||||
|
StorageKind = storageKind;
|
||||||
|
IoVariable = ioVariable;
|
||||||
|
Location = location;
|
||||||
|
Component = component;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object other)
|
||||||
|
{
|
||||||
|
return other is IoDefinition ioDefinition && Equals(ioDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IoDefinition other)
|
||||||
|
{
|
||||||
|
return StorageKind == other.StorageKind &&
|
||||||
|
IoVariable == other.IoVariable &&
|
||||||
|
Location == other.Location &&
|
||||||
|
Component == other.Component;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return (int)StorageKind | ((int)IoVariable << 8) | (Location << 16) | (Component << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{StorageKind}.{IoVariable}.{Location}.{Component}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
return type switch
|
return type switch
|
||||||
{
|
{
|
||||||
OperandType.Argument => AggregateType.S32,
|
OperandType.Argument => AggregateType.S32,
|
||||||
OperandType.Attribute => AggregateType.FP32,
|
|
||||||
OperandType.AttributePerPatch => AggregateType.FP32,
|
|
||||||
OperandType.Constant => AggregateType.S32,
|
OperandType.Constant => AggregateType.S32,
|
||||||
OperandType.ConstantBuffer => AggregateType.FP32,
|
OperandType.ConstantBuffer => AggregateType.FP32,
|
||||||
OperandType.Undefined => AggregateType.S32,
|
OperandType.Undefined => AggregateType.S32,
|
||||||
|
@@ -65,49 +65,35 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
context.LeaveFunction();
|
context.LeaveFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline)
|
|
||||||
{
|
|
||||||
for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
|
|
||||||
{
|
|
||||||
var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
|
|
||||||
var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
|
|
||||||
|
|
||||||
for (int i = 0; i < locations.Length; i++)
|
|
||||||
{
|
|
||||||
byte location = locations[i];
|
|
||||||
if (location < 0xc0)
|
|
||||||
{
|
|
||||||
context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.Info;
|
return context.Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddOperation(StructuredProgramContext context, Operation operation)
|
private static void AddOperation(StructuredProgramContext context, Operation operation)
|
||||||
{
|
{
|
||||||
Instruction inst = operation.Inst;
|
Instruction inst = operation.Inst;
|
||||||
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
if (inst == Instruction.LoadAttribute)
|
if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput())
|
||||||
{
|
{
|
||||||
Operand src1 = operation.GetSource(0);
|
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
|
||||||
Operand src2 = operation.GetSource(1);
|
bool isOutput = storageKind.IsOutput();
|
||||||
|
bool perPatch = storageKind.IsPerPatch();
|
||||||
|
int location = 0;
|
||||||
|
int component = 0;
|
||||||
|
|
||||||
if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant)
|
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||||
{
|
{
|
||||||
int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2);
|
location = operation.GetSource(1).Value;
|
||||||
|
|
||||||
if ((src1.Value & AttributeConsts.LoadOutputMask) != 0)
|
if (operation.SourcesCount > 2 &&
|
||||||
|
operation.GetSource(2).Type == OperandType.Constant &&
|
||||||
|
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
|
||||||
{
|
{
|
||||||
context.Info.Outputs.Add(attrOffset);
|
component = operation.GetSource(2).Value;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Info.Inputs.Add(attrOffset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool vectorDest = IsVectorDestInst(inst);
|
bool vectorDest = IsVectorDestInst(inst);
|
||||||
@@ -119,12 +105,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
for (int index = 0; index < operation.SourcesCount; index++)
|
||||||
{
|
{
|
||||||
sources[index] = context.GetOperandUse(operation.GetSource(index));
|
sources[index] = context.GetOperand(operation.GetSource(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < outDestsCount; index++)
|
for (int index = 0; index < outDestsCount; index++)
|
||||||
{
|
{
|
||||||
AstOperand oper = context.GetOperandDef(operation.GetDest(1 + index));
|
AstOperand oper = context.GetOperand(operation.GetDest(1 + index));
|
||||||
|
|
||||||
oper.VarType = InstructionInfo.GetSrcVarType(inst, sourcesCount + index);
|
oper.VarType = InstructionInfo.GetSrcVarType(inst, sourcesCount + index);
|
||||||
|
|
||||||
@@ -163,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
|
source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
AggregateType destElemType = destType;
|
AggregateType destElemType = destType;
|
||||||
@@ -181,17 +167,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
for (int i = 0; i < operation.DestsCount; i++)
|
for (int i = 0; i < operation.DestsCount; i++)
|
||||||
{
|
{
|
||||||
AstOperand dest = context.GetOperandDef(operation.GetDest(i));
|
AstOperand dest = context.GetOperand(operation.GetDest(i));
|
||||||
AstOperand index = new AstOperand(OperandType.Constant, i);
|
AstOperand index = new AstOperand(OperandType.Constant, i);
|
||||||
|
|
||||||
dest.VarType = destElemType;
|
dest.VarType = destElemType;
|
||||||
|
|
||||||
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, new[] { destVec, index }, 2)));
|
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (operation.Dest != null)
|
else if (operation.Dest != null)
|
||||||
{
|
{
|
||||||
AstOperand dest = context.GetOperandDef(operation.Dest);
|
AstOperand dest = context.GetOperand(operation.Dest);
|
||||||
|
|
||||||
// If all the sources are bool, it's better to use short-circuiting
|
// If all the sources are bool, it's better to use short-circuiting
|
||||||
// logical operations, rather than forcing a cast to int and doing
|
// logical operations, rather than forcing a cast to int and doing
|
||||||
@@ -234,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else if (!isCopy)
|
else if (!isCopy)
|
||||||
{
|
{
|
||||||
source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
|
source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -255,7 +241,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.AddNode(new AstOperation(inst, operation.Index, sources, operation.SourcesCount));
|
context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Those instructions needs to be emulated by using helper functions,
|
// Those instructions needs to be emulated by using helper functions,
|
||||||
@@ -263,13 +249,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
// decide which helper functions are needed on the final generated code.
|
// decide which helper functions are needed on the final generated code.
|
||||||
switch (operation.Inst)
|
switch (operation.Inst)
|
||||||
{
|
{
|
||||||
case Instruction.AtomicMaxS32 | Instruction.MrShared:
|
case Instruction.AtomicMaxS32:
|
||||||
case Instruction.AtomicMinS32 | Instruction.MrShared:
|
case Instruction.AtomicMinS32:
|
||||||
|
if (operation.StorageKind == StorageKind.SharedMemory)
|
||||||
|
{
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
|
||||||
break;
|
}
|
||||||
case Instruction.AtomicMaxS32 | Instruction.MrStorage:
|
else if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
case Instruction.AtomicMinS32 | Instruction.MrStorage:
|
{
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Instruction.MultiplyHighS32:
|
case Instruction.MultiplyHighS32:
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
|
||||||
|
@@ -37,43 +37,26 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
Config = config;
|
Config = config;
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.TessellationControl)
|
if (config.GpPassthrough)
|
||||||
{
|
|
||||||
// Required to index outputs.
|
|
||||||
Info.Inputs.Add(AttributeConsts.InvocationId);
|
|
||||||
}
|
|
||||||
else if (config.GpPassthrough)
|
|
||||||
{
|
{
|
||||||
int passthroughAttributes = config.PassthroughAttributes;
|
int passthroughAttributes = config.PassthroughAttributes;
|
||||||
while (passthroughAttributes != 0)
|
while (passthroughAttributes != 0)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
|
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
|
||||||
|
|
||||||
int attrBase = AttributeConsts.UserAttributeBase + index * 16;
|
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.UserDefined, index));
|
||||||
Info.Inputs.Add(attrBase);
|
|
||||||
Info.Inputs.Add(attrBase + 4);
|
|
||||||
Info.Inputs.Add(attrBase + 8);
|
|
||||||
Info.Inputs.Add(attrBase + 12);
|
|
||||||
|
|
||||||
passthroughAttributes &= ~(1 << index);
|
passthroughAttributes &= ~(1 << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Info.Inputs.Add(AttributeConsts.PositionX);
|
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.Position));
|
||||||
Info.Inputs.Add(AttributeConsts.PositionY);
|
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.PointSize));
|
||||||
Info.Inputs.Add(AttributeConsts.PositionZ);
|
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.ClipDistance));
|
||||||
Info.Inputs.Add(AttributeConsts.PositionW);
|
|
||||||
Info.Inputs.Add(AttributeConsts.PointSize);
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
Info.Inputs.Add(AttributeConsts.ClipDistance0 + i * 4);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (config.Stage == ShaderStage.Fragment)
|
else if (config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
// Potentially used for texture coordinate scaling.
|
// Potentially used for texture coordinate scaling.
|
||||||
Info.Inputs.Add(AttributeConsts.PositionX);
|
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord));
|
||||||
Info.Inputs.Add(AttributeConsts.PositionY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cond = GetOperandUse(branchOp.GetSource(0));
|
cond = GetOperand(branchOp.GetSource(0));
|
||||||
|
|
||||||
Instruction invInst = type == AstBlockType.If
|
Instruction invInst = type == AstBlockType.If
|
||||||
? Instruction.BranchIfTrue
|
? Instruction.BranchIfTrue
|
||||||
@@ -315,41 +298,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
return newTemp;
|
return newTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstOperand GetOperandDef(Operand operand)
|
public AstOperand GetOperand(Operand operand)
|
||||||
{
|
|
||||||
if (operand.Type == OperandType.Attribute)
|
|
||||||
{
|
|
||||||
Info.Outputs.Add(operand.Value & AttributeConsts.Mask);
|
|
||||||
}
|
|
||||||
else if (operand.Type == OperandType.AttributePerPatch)
|
|
||||||
{
|
|
||||||
Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetOperand(operand);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AstOperand GetOperandUse(Operand operand)
|
|
||||||
{
|
|
||||||
// If this flag is set, we're reading from an output attribute instead.
|
|
||||||
if (operand.Type.IsAttribute() && (operand.Value & AttributeConsts.LoadOutputMask) != 0)
|
|
||||||
{
|
|
||||||
return GetOperandDef(operand);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operand.Type == OperandType.Attribute)
|
|
||||||
{
|
|
||||||
Info.Inputs.Add(operand.Value);
|
|
||||||
}
|
|
||||||
else if (operand.Type == OperandType.AttributePerPatch)
|
|
||||||
{
|
|
||||||
Info.InputsPerPatch.Add(operand.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetOperand(operand);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AstOperand GetOperand(Operand operand)
|
|
||||||
{
|
{
|
||||||
if (operand == null)
|
if (operand == null)
|
||||||
{
|
{
|
||||||
|
@@ -22,60 +22,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
public List<StructuredFunction> Functions { get; }
|
public List<StructuredFunction> Functions { get; }
|
||||||
|
|
||||||
public HashSet<int> Inputs { get; }
|
public HashSet<IoDefinition> IoDefinitions { get; }
|
||||||
public HashSet<int> Outputs { get; }
|
|
||||||
public HashSet<int> InputsPerPatch { get; }
|
|
||||||
public HashSet<int> OutputsPerPatch { get; }
|
|
||||||
|
|
||||||
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
||||||
|
|
||||||
public TransformFeedbackOutput[] TransformFeedbackOutputs { get; }
|
|
||||||
|
|
||||||
public StructuredProgramInfo()
|
public StructuredProgramInfo()
|
||||||
{
|
{
|
||||||
Functions = new List<StructuredFunction>();
|
Functions = new List<StructuredFunction>();
|
||||||
|
|
||||||
Inputs = new HashSet<int>();
|
IoDefinitions = new HashSet<IoDefinition>();
|
||||||
Outputs = new HashSet<int>();
|
|
||||||
InputsPerPatch = new HashSet<int>();
|
|
||||||
OutputsPerPatch = new HashSet<int>();
|
|
||||||
|
|
||||||
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
|
|
||||||
{
|
|
||||||
int index = attr / 4;
|
|
||||||
return TransformFeedbackOutputs[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetTransformFeedbackOutputComponents(int attr)
|
|
||||||
{
|
|
||||||
int index = attr / 4;
|
|
||||||
int baseIndex = index & ~3;
|
|
||||||
|
|
||||||
int count = 1;
|
|
||||||
|
|
||||||
for (; count < 4; count++)
|
|
||||||
{
|
|
||||||
ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1];
|
|
||||||
ref var curr = ref TransformFeedbackOutputs[baseIndex + count];
|
|
||||||
|
|
||||||
int prevOffset = prev.Offset;
|
|
||||||
int currOffset = curr.Offset;
|
|
||||||
|
|
||||||
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseIndex + count <= index)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,36 +2,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
static class AttributeConsts
|
static class AttributeConsts
|
||||||
{
|
{
|
||||||
public const int TessLevelOuter0 = 0x000;
|
|
||||||
public const int TessLevelOuter1 = 0x004;
|
|
||||||
public const int TessLevelOuter2 = 0x008;
|
|
||||||
public const int TessLevelOuter3 = 0x00c;
|
|
||||||
public const int TessLevelInner0 = 0x010;
|
|
||||||
public const int TessLevelInner1 = 0x014;
|
|
||||||
public const int PrimitiveId = 0x060;
|
public const int PrimitiveId = 0x060;
|
||||||
public const int Layer = 0x064;
|
public const int Layer = 0x064;
|
||||||
public const int ViewportIndex = 0x068;
|
|
||||||
public const int PointSize = 0x06c;
|
|
||||||
public const int PositionX = 0x070;
|
public const int PositionX = 0x070;
|
||||||
public const int PositionY = 0x074;
|
public const int PositionY = 0x074;
|
||||||
public const int PositionZ = 0x078;
|
|
||||||
public const int PositionW = 0x07c;
|
|
||||||
public const int FrontColorDiffuseR = 0x280;
|
public const int FrontColorDiffuseR = 0x280;
|
||||||
public const int FrontColorDiffuseG = 0x284;
|
|
||||||
public const int FrontColorDiffuseB = 0x288;
|
|
||||||
public const int FrontColorDiffuseA = 0x28c;
|
|
||||||
public const int FrontColorSpecularR = 0x290;
|
|
||||||
public const int FrontColorSpecularG = 0x294;
|
|
||||||
public const int FrontColorSpecularB = 0x298;
|
|
||||||
public const int FrontColorSpecularA = 0x29c;
|
|
||||||
public const int BackColorDiffuseR = 0x2a0;
|
public const int BackColorDiffuseR = 0x2a0;
|
||||||
public const int BackColorDiffuseG = 0x2a4;
|
|
||||||
public const int BackColorDiffuseB = 0x2a8;
|
|
||||||
public const int BackColorDiffuseA = 0x2ac;
|
|
||||||
public const int BackColorSpecularR = 0x2b0;
|
|
||||||
public const int BackColorSpecularG = 0x2b4;
|
|
||||||
public const int BackColorSpecularB = 0x2b8;
|
|
||||||
public const int BackColorSpecularA = 0x2bc;
|
|
||||||
public const int ClipDistance0 = 0x2c0;
|
public const int ClipDistance0 = 0x2c0;
|
||||||
public const int ClipDistance1 = 0x2c4;
|
public const int ClipDistance1 = 0x2c4;
|
||||||
public const int ClipDistance2 = 0x2c8;
|
public const int ClipDistance2 = 0x2c8;
|
||||||
@@ -40,8 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public const int ClipDistance5 = 0x2d4;
|
public const int ClipDistance5 = 0x2d4;
|
||||||
public const int ClipDistance6 = 0x2d8;
|
public const int ClipDistance6 = 0x2d8;
|
||||||
public const int ClipDistance7 = 0x2dc;
|
public const int ClipDistance7 = 0x2dc;
|
||||||
public const int PointCoordX = 0x2e0;
|
public const int FogCoord = 0x2e8;
|
||||||
public const int PointCoordY = 0x2e4;
|
|
||||||
public const int TessCoordX = 0x2f0;
|
public const int TessCoordX = 0x2f0;
|
||||||
public const int TessCoordY = 0x2f4;
|
public const int TessCoordY = 0x2f4;
|
||||||
public const int InstanceId = 0x2f8;
|
public const int InstanceId = 0x2f8;
|
||||||
@@ -57,49 +32,5 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public const int UserAttributePerPatchBase = 0x18;
|
public const int UserAttributePerPatchBase = 0x18;
|
||||||
public const int UserAttributePerPatchEnd = 0x200;
|
public const int UserAttributePerPatchEnd = 0x200;
|
||||||
|
|
||||||
public const int LoadOutputMask = 1 << 30;
|
|
||||||
public const int Mask = 0x3fffffff;
|
|
||||||
|
|
||||||
|
|
||||||
// Note: Those attributes are used internally by the translator
|
|
||||||
// only, they don't exist on Maxwell.
|
|
||||||
public const int SpecialMask = 0xf << 24;
|
|
||||||
public const int FragmentOutputDepth = 0x1000000;
|
|
||||||
public const int FragmentOutputColorBase = 0x1000010;
|
|
||||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
|
||||||
|
|
||||||
public const int FragmentOutputIsBgraBase = 0x1000100;
|
|
||||||
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
|
|
||||||
|
|
||||||
public const int SupportBlockViewInverseX = 0x1000200;
|
|
||||||
public const int SupportBlockViewInverseY = 0x1000204;
|
|
||||||
|
|
||||||
public const int ThreadIdX = 0x2000000;
|
|
||||||
public const int ThreadIdY = 0x2000004;
|
|
||||||
public const int ThreadIdZ = 0x2000008;
|
|
||||||
|
|
||||||
public const int CtaIdX = 0x2000010;
|
|
||||||
public const int CtaIdY = 0x2000014;
|
|
||||||
public const int CtaIdZ = 0x2000018;
|
|
||||||
|
|
||||||
public const int LaneId = 0x2000020;
|
|
||||||
|
|
||||||
public const int InvocationId = 0x2000024;
|
|
||||||
public const int PatchVerticesIn = 0x2000028;
|
|
||||||
|
|
||||||
public const int EqMask = 0x2000030;
|
|
||||||
public const int GeMask = 0x2000034;
|
|
||||||
public const int GtMask = 0x2000038;
|
|
||||||
public const int LeMask = 0x200003c;
|
|
||||||
public const int LtMask = 0x2000040;
|
|
||||||
|
|
||||||
public const int ThreadKill = 0x2000044;
|
|
||||||
|
|
||||||
public const int BaseInstance = 0x2000050;
|
|
||||||
public const int BaseVertex = 0x2000054;
|
|
||||||
public const int InstanceIndex = 0x2000058;
|
|
||||||
public const int VertexIndex = 0x200005c;
|
|
||||||
public const int DrawIndex = 0x2000060;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,210 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
|
||||||
{
|
|
||||||
readonly struct AttributeInfo
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
|
|
||||||
{
|
|
||||||
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
|
|
||||||
|
|
||||||
// Special.
|
|
||||||
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
|
|
||||||
{
|
|
||||||
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
};
|
|
||||||
|
|
||||||
public int BaseValue { get; }
|
|
||||||
public int Value { get; }
|
|
||||||
public int Length { get; }
|
|
||||||
public AggregateType Type { get; }
|
|
||||||
public bool IsBuiltin { get; }
|
|
||||||
public bool IsValid => Type != AggregateType.Invalid;
|
|
||||||
|
|
||||||
public AttributeInfo(int baseValue, int index, int length, AggregateType type, bool isBuiltin = true)
|
|
||||||
{
|
|
||||||
BaseValue = baseValue;
|
|
||||||
Value = baseValue + index * 4;
|
|
||||||
Length = length;
|
|
||||||
Type = type;
|
|
||||||
IsBuiltin = isBuiltin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetInnermostIndex()
|
|
||||||
{
|
|
||||||
return (Value - BaseValue) / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch)
|
|
||||||
{
|
|
||||||
return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return From(config, value, isOutAttr).IsValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
return FromPatch(config, value, isOutAttr).IsValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
value &= ~3;
|
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
int location = (value - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
AggregateType elemType;
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Vertex && !isOutAttr)
|
|
||||||
{
|
|
||||||
elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elemType = AggregateType.FP32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
|
|
||||||
}
|
|
||||||
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
|
|
||||||
{
|
|
||||||
int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
|
|
||||||
var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
|
|
||||||
{
|
|
||||||
AttributeType.Sint => AggregateType.S32,
|
|
||||||
AttributeType.Uint => AggregateType.U32,
|
|
||||||
_ => AggregateType.FP32
|
|
||||||
};
|
|
||||||
|
|
||||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
|
|
||||||
}
|
|
||||||
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
|
|
||||||
{
|
|
||||||
return new AttributeInfo(value, 0, 1, AggregateType.FP32);
|
|
||||||
}
|
|
||||||
else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info))
|
|
||||||
{
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
value &= ~3;
|
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
|
||||||
{
|
|
||||||
int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
|
|
||||||
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false);
|
|
||||||
}
|
|
||||||
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
|
|
||||||
{
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsArrayBuiltIn(int attr)
|
|
||||||
{
|
|
||||||
if (attr <= AttributeConsts.TessLevelInner1 ||
|
|
||||||
attr == AttributeConsts.TessCoordX ||
|
|
||||||
attr == AttributeConsts.TessCoordY)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (attr & AttributeConsts.SpecialMask) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (isOutAttr)
|
|
||||||
{
|
|
||||||
return stage == ShaderStage.TessellationControl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return stage == ShaderStage.TessellationControl ||
|
|
||||||
stage == ShaderStage.TessellationEvaluation ||
|
|
||||||
stage == ShaderStage.Geometry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsArrayAttributeSpirv(ShaderStage stage, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (isOutAttr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return stage == ShaderStage.TessellationControl ||
|
|
||||||
stage == ShaderStage.TessellationEvaluation ||
|
|
||||||
stage == ShaderStage.Geometry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user