Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9ef94c8292 | ||
|
915d6d044c | ||
|
a4780ab33b | ||
|
a947a45d81 | ||
|
9db73f74cf | ||
|
a1efd87c45 | ||
|
49be977588 | ||
|
c95be55091 | ||
|
63dedbda86 | ||
|
c532118d94 | ||
|
52d6f2e656 | ||
|
c9bc4eaf58 | ||
|
3249f8ff41 | ||
|
1b41b285ac | ||
|
f5a6f45b27 | ||
|
210557951b |
@@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
|
|||||||
|
|
||||||
#### C# Coding Conventions ####
|
#### C# Coding Conventions ####
|
||||||
|
|
||||||
|
# Namespace preferences
|
||||||
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
|
resharper_csharp_namespace_body = block_scoped
|
||||||
|
|
||||||
# var preferences
|
# var preferences
|
||||||
csharp_style_var_elsewhere = false:silent
|
csharp_style_var_elsewhere = false:silent
|
||||||
csharp_style_var_for_built_in_types = false:silent
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
|
@@ -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,21 +260,23 @@ 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);
|
||||||
|
|
||||||
|
context.Assembler.Mov(memOp, bits, OperandType.I32);
|
||||||
|
context.Assembler.Ldmxcsr(memOp);
|
||||||
}
|
}
|
||||||
else /* if (intrinOp.Intrinsic == Intrinsic.X86Mxcsrub) */
|
else if (operation.Intrinsic == Intrinsic.X86Stmxcsr)
|
||||||
{
|
{
|
||||||
Operand notBits = Const(~bits.AsInt32());
|
Operand dest = operation.Destination;
|
||||||
|
Debug.Assert(dest.Type == OperandType.I32);
|
||||||
|
|
||||||
context.Assembler.And(memOp, notBits, OperandType.I32);
|
context.Assembler.Stmxcsr(memOp);
|
||||||
|
context.Assembler.Mov(dest, memOp, OperandType.I32);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Assembler.Ldmxcsr(memOp);
|
|
||||||
|
|
||||||
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,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
namespace ARMeilleure.Decoders;
|
namespace ARMeilleure.Decoders
|
||||||
|
|
||||||
interface IOpCode32Exception
|
|
||||||
{
|
{
|
||||||
int Id { get; }
|
interface IOpCode32Exception
|
||||||
|
{
|
||||||
|
int Id { get; }
|
||||||
|
}
|
||||||
}
|
}
|
@@ -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);
|
||||||
|
@@ -615,14 +615,11 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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;
|
|
||||||
|
|
||||||
Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd;
|
Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd;
|
||||||
|
|
||||||
return context.AddIntrinsic(addInst, op1, op2);
|
return context.AddIntrinsic(addInst, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -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.X86Addss, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
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.X86Addsd, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@@ -730,10 +743,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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
|
||||||
@@ -755,10 +765,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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
|
||||||
@@ -886,10 +893,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -914,10 +918,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -940,10 +941,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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
|
||||||
@@ -965,10 +963,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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
|
||||||
@@ -1096,10 +1091,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1124,10 +1116,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -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.X86Addps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
|
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.X86Addpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
|
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));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, 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));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, 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.X86Subps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
|
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.X86Subpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
|
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));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, 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));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, 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.X86Subss, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
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.X86Subsd, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0f);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0f);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
}
|
||||||
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) */
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0d);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0d);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
}
|
||||||
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)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0f);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0f);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
}
|
||||||
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) */
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0d);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0d);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
}
|
||||||
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,20 +2296,32 @@ 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)
|
||||||
EmitScalarUnaryOpF(context, (op1) =>
|
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintxS);
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF(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)
|
||||||
EmitVectorUnaryOpF(context, (op1) =>
|
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintxV);
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpF(context, (op1) =>
|
||||||
|
{
|
||||||
|
return EmitRoundByRMode(context, op1);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Frintz_S(ArmEmitterContext context)
|
public static void Frintz_S(ArmEmitterContext context)
|
||||||
@@ -2237,16 +2432,25 @@ 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);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
||||||
@@ -2257,10 +2461,17 @@ 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);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
||||||
@@ -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;
|
||||||
@@ -4834,10 +5014,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
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)
|
||||||
@@ -4872,10 +5049,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -542,10 +546,17 @@ namespace ARMeilleure.Instructions
|
|||||||
// VRINTX (floating-point).
|
// VRINTX (floating-point).
|
||||||
public static void Vrintx_S(ArmEmitterContext context)
|
public static void Vrintx_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF32(context, (op1) =>
|
if (Optimizations.UseAdvSimd)
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintxS);
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF32(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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,17 +3,17 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
<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.12.11" />
|
<PackageVersion Include="DynamicData" Version="7.13.1" />
|
||||||
<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,10 +44,10 @@
|
|||||||
<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.27.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" />
|
||||||
<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.0" />
|
||||||
<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.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
{
|
{
|
||||||
var localeStrings = new Dictionary<LocaleKeys, string>();
|
var localeStrings = new Dictionary<LocaleKeys, string>();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
foreach (var item in strings)
|
foreach (var item in strings)
|
||||||
{
|
{
|
||||||
|
@@ -4,13 +4,14 @@ using FluentAvalonia.UI.Controls;
|
|||||||
using ICSharpCode.SharpZipLib.GZip;
|
using ICSharpCode.SharpZipLib.GZip;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Ryujinx.Ava;
|
using Ryujinx.Ava;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
using Ryujinx.Ui.Common.Models.Github;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -31,6 +32,7 @@ namespace Ryujinx.Modules
|
|||||||
internal static class Updater
|
internal static class Updater
|
||||||
{
|
{
|
||||||
private const string GitHubApiURL = "https://api.github.com";
|
private const string GitHubApiURL = "https://api.github.com";
|
||||||
|
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||||
@@ -99,22 +101,16 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
|
||||||
JToken assets = jsonRoot["assets"];
|
_buildVer = fetched.Name;
|
||||||
|
|
||||||
_buildVer = (string)jsonRoot["name"];
|
foreach (var asset in fetched.Assets)
|
||||||
|
|
||||||
foreach (JToken asset in assets)
|
|
||||||
{
|
{
|
||||||
string assetName = (string)asset["name"];
|
if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt))
|
||||||
string assetState = (string)asset["state"];
|
|
||||||
string downloadURL = (string)asset["browser_download_url"];
|
|
||||||
|
|
||||||
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
|
|
||||||
{
|
{
|
||||||
_buildUrl = downloadURL;
|
_buildUrl = asset.BrowserDownloadUrl;
|
||||||
|
|
||||||
if (assetState != "uploaded")
|
if (asset.State != "uploaded")
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
|
@@ -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,
|
||||||
|
@@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
|
||||||
{
|
|
||||||
public class Amiibo
|
|
||||||
{
|
|
||||||
public struct AmiiboJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; }
|
|
||||||
[JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AmiiboApi
|
|
||||||
{
|
|
||||||
[JsonPropertyName("name")] public string Name { get; set; }
|
|
||||||
[JsonPropertyName("head")] public string Head { get; set; }
|
|
||||||
[JsonPropertyName("tail")] public string Tail { get; set; }
|
|
||||||
[JsonPropertyName("image")] public string Image { get; set; }
|
|
||||||
[JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; }
|
|
||||||
[JsonPropertyName("character")] public string Character { get; set; }
|
|
||||||
[JsonPropertyName("gameSeries")] public string GameSeries { get; set; }
|
|
||||||
[JsonPropertyName("type")] public string Type { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetId()
|
|
||||||
{
|
|
||||||
return Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is AmiiboApi amiibo)
|
|
||||||
{
|
|
||||||
return amiibo.Head + amiibo.Tail == Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiGamesSwitch
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameID")] public List<string> GameId { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameName")] public string GameName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiUsage
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Usage")] public string Usage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("write")] public bool Write { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||||
|
|
||||||
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n";
|
Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)) + "\n\n";
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -4,11 +4,11 @@ using Avalonia.Media.Imaging;
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
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.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -17,6 +17,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
@@ -31,8 +32,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private readonly StyleableWindow _owner;
|
private readonly StyleableWindow _owner;
|
||||||
|
|
||||||
private Bitmap _amiiboImage;
|
private Bitmap _amiiboImage;
|
||||||
private List<Amiibo.AmiiboApi> _amiiboList;
|
private List<AmiiboApi> _amiiboList;
|
||||||
private AvaloniaList<Amiibo.AmiiboApi> _amiibos;
|
private AvaloniaList<AmiiboApi> _amiibos;
|
||||||
private ObservableCollection<string> _amiiboSeries;
|
private ObservableCollection<string> _amiiboSeries;
|
||||||
|
|
||||||
private int _amiiboSelectedIndex;
|
private int _amiiboSelectedIndex;
|
||||||
@@ -41,6 +42,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _showAllAmiibo;
|
private bool _showAllAmiibo;
|
||||||
private bool _useRandomUuid;
|
private bool _useRandomUuid;
|
||||||
private string _usage;
|
private string _usage;
|
||||||
|
|
||||||
|
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
||||||
{
|
{
|
||||||
@@ -52,9 +55,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
||||||
|
|
||||||
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
||||||
_amiiboList = new List<Amiibo.AmiiboApi>();
|
_amiiboList = new List<AmiiboApi>();
|
||||||
_amiiboSeries = new ObservableCollection<string>();
|
_amiiboSeries = new ObservableCollection<string>();
|
||||||
_amiibos = new AvaloniaList<Amiibo.AmiiboApi>();
|
_amiibos = new AvaloniaList<AmiiboApi>();
|
||||||
|
|
||||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AvaloniaList<Amiibo.AmiiboApi> AmiiboList
|
public AvaloniaList<AmiiboApi> AmiiboList
|
||||||
{
|
{
|
||||||
get => _amiibos;
|
get => _amiibos;
|
||||||
set
|
set
|
||||||
@@ -187,9 +190,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (File.Exists(_amiiboJsonPath))
|
if (File.Exists(_amiiboJsonPath))
|
||||||
{
|
{
|
||||||
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@@ -206,7 +209,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
|
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
ParseAmiiboData();
|
ParseAmiiboData();
|
||||||
@@ -223,7 +226,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!ShowAllAmiibo)
|
if (!ShowAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@@ -255,7 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private void SelectLastScannedAmiibo()
|
private void SelectLastScannedAmiibo()
|
||||||
{
|
{
|
||||||
Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
||||||
|
|
||||||
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
|
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
|
||||||
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
|
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
|
||||||
@@ -270,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList
|
List<AmiiboApi> amiiboSortedList = _amiiboList
|
||||||
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
|
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
|
||||||
.OrderBy(amiibo => amiibo.Name).ToList();
|
.OrderBy(amiibo => amiibo.Name).ToList();
|
||||||
|
|
||||||
@@ -280,7 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!_showAllAmiibo)
|
if (!_showAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@@ -314,7 +317,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
||||||
|
|
||||||
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
||||||
|
|
||||||
@@ -326,11 +329,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
bool writable = false;
|
bool writable = false;
|
||||||
|
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (item.GameId.Contains(TitleId))
|
if (item.GameId.Contains(TitleId))
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage)
|
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
||||||
{
|
{
|
||||||
usageString += Environment.NewLine +
|
usageString += Environment.NewLine +
|
||||||
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
||||||
|
@@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _isLoaded;
|
private bool _isLoaded;
|
||||||
private readonly UserControl _owner;
|
private readonly UserControl _owner;
|
||||||
|
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
||||||
public IGamepad SelectedGamepad { get; private set; }
|
public IGamepad SelectedGamepad { get; private set; }
|
||||||
|
|
||||||
@@ -706,10 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream stream = File.OpenRead(path))
|
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
||||||
{
|
|
||||||
config = JsonHelper.Deserialize<InputConfig>(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (JsonException) { }
|
catch (JsonException) { }
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
@@ -775,7 +774,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
|
|
||||||
string jsonString = JsonHelper.Serialize(config, true);
|
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
|
||||||
|
|
||||||
await File.WriteAllTextAsync(path, jsonString);
|
await File.WriteAllTextAsync(path, jsonString);
|
||||||
|
|
||||||
|
@@ -21,7 +21,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
@@ -41,6 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private ulong _titleId;
|
private ulong _titleId;
|
||||||
private string _titleName;
|
private string _titleName;
|
||||||
|
|
||||||
|
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
||||||
{
|
{
|
||||||
get => _downloadableContents;
|
get => _downloadableContents;
|
||||||
@@ -100,7 +101,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath);
|
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -330,10 +331,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
_downloadableContentContainerList.Add(container);
|
_downloadableContentContainerList.Add(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough))
|
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer);
|
||||||
{
|
|
||||||
downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -22,230 +22,231 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels;
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public class TitleUpdateViewModel : BaseModel
|
|
||||||
{
|
{
|
||||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
public class TitleUpdateViewModel : BaseModel
|
||||||
public readonly string _titleUpdateJsonPath;
|
|
||||||
private VirtualFileSystem _virtualFileSystem { get; }
|
|
||||||
private ulong _titleId { get; }
|
|
||||||
private string _titleName { get; }
|
|
||||||
|
|
||||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
|
||||||
private AvaloniaList<object> _views = new();
|
|
||||||
private object _selectedUpdate;
|
|
||||||
|
|
||||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
|
||||||
{
|
{
|
||||||
get => _titleUpdates;
|
public TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
set
|
public readonly string _titleUpdateJsonPath;
|
||||||
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
|
private ulong _titleId { get; }
|
||||||
|
private string _titleName { get; }
|
||||||
|
|
||||||
|
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||||
|
private AvaloniaList<object> _views = new();
|
||||||
|
private object _selectedUpdate;
|
||||||
|
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
|
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
||||||
{
|
{
|
||||||
_titleUpdates = value;
|
get => _titleUpdates;
|
||||||
OnPropertyChanged();
|
set
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AvaloniaList<object> Views
|
|
||||||
{
|
|
||||||
get => _views;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_views = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public object SelectedUpdate
|
|
||||||
{
|
|
||||||
get => _selectedUpdate;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_selectedUpdate = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
|
||||||
{
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
|
|
||||||
_titleId = titleId;
|
|
||||||
_titleName = titleName;
|
|
||||||
|
|
||||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
|
||||||
|
|
||||||
_titleUpdateWindowData = new TitleUpdateMetadata
|
|
||||||
{
|
{
|
||||||
Selected = "",
|
_titleUpdates = value;
|
||||||
Paths = new List<string>()
|
OnPropertyChanged();
|
||||||
};
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadUpdates()
|
|
||||||
{
|
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
|
||||||
{
|
|
||||||
AddUpdate(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
|
||||||
|
|
||||||
SelectedUpdate = selected;
|
|
||||||
|
|
||||||
// NOTE: Save the list again to remove leftovers.
|
|
||||||
Save();
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortUpdates()
|
|
||||||
{
|
|
||||||
var list = TitleUpdates.ToList();
|
|
||||||
|
|
||||||
list.Sort((first, second) =>
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
Views.Clear();
|
|
||||||
Views.Add(new BaseModel());
|
|
||||||
Views.AddRange(list);
|
|
||||||
|
|
||||||
if (SelectedUpdate == null)
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
|
||||||
else if (!TitleUpdates.Contains(SelectedUpdate))
|
|
||||||
{
|
|
||||||
if (Views.Count > 1)
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void AddUpdate(string path)
|
public AvaloniaList<object> Views
|
||||||
{
|
|
||||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
|
||||||
{
|
{
|
||||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
get => _views;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_views = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SelectedUpdate
|
||||||
|
{
|
||||||
|
get => _selectedUpdate;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_selectedUpdate = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
|
{
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
|
_titleId = titleId;
|
||||||
|
_titleName = titleName;
|
||||||
|
|
||||||
|
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
_titleUpdateWindowData = new TitleUpdateMetadata
|
||||||
{
|
{
|
||||||
ApplicationControlProperty controlData = new();
|
Selected = "",
|
||||||
|
Paths = new List<string>()
|
||||||
|
};
|
||||||
|
|
||||||
using UniqueRef<IFile> nacpFile = new();
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
LoadUpdates();
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
}
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
private void LoadUpdates()
|
||||||
|
{
|
||||||
|
foreach (string path in _titleUpdateWindowData.Paths)
|
||||||
|
{
|
||||||
|
AddUpdate(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
||||||
|
|
||||||
|
SelectedUpdate = selected;
|
||||||
|
|
||||||
|
// NOTE: Save the list again to remove leftovers.
|
||||||
|
Save();
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortUpdates()
|
||||||
|
{
|
||||||
|
var list = TitleUpdates.ToList();
|
||||||
|
|
||||||
|
list.Sort((first, second) =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
Views.Clear();
|
||||||
|
Views.Add(new BaseModel());
|
||||||
|
Views.AddRange(list);
|
||||||
|
|
||||||
|
if (SelectedUpdate == null)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[0];
|
||||||
|
}
|
||||||
|
else if (!TitleUpdates.Contains(SelectedUpdate))
|
||||||
|
{
|
||||||
|
if (Views.Count > 1)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddUpdate(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
||||||
|
{
|
||||||
|
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
||||||
|
|
||||||
|
if (controlNca != null && patchNca != null)
|
||||||
|
{
|
||||||
|
ApplicationControlProperty controlData = new();
|
||||||
|
|
||||||
|
using UniqueRef<IFile> nacpFile = new();
|
||||||
|
|
||||||
|
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
|
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveUpdate(TitleUpdateModel update)
|
public void RemoveUpdate(TitleUpdateModel update)
|
||||||
{
|
|
||||||
TitleUpdates.Remove(update);
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Add()
|
|
||||||
{
|
|
||||||
OpenFileDialog dialog = new()
|
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
TitleUpdates.Remove(update);
|
||||||
AllowMultiple = true
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Filters.Add(new FileDialogFilter
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Add()
|
||||||
{
|
{
|
||||||
Name = "NSP",
|
OpenFileDialog dialog = new()
|
||||||
Extensions = { "nsp" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
{
|
||||||
foreach (string file in files)
|
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||||
|
AllowMultiple = true
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.Filters.Add(new FileDialogFilter
|
||||||
|
{
|
||||||
|
Name = "NSP",
|
||||||
|
Extensions = { "nsp" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
{
|
{
|
||||||
AddUpdate(file);
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
AddUpdate(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
SortUpdates();
|
public void Save()
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Paths.Clear();
|
|
||||||
_titleUpdateWindowData.Selected = "";
|
|
||||||
|
|
||||||
foreach (TitleUpdateModel update in TitleUpdates)
|
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
_titleUpdateWindowData.Paths.Clear();
|
||||||
|
_titleUpdateWindowData.Selected = "";
|
||||||
|
|
||||||
if (update == SelectedUpdate)
|
foreach (TitleUpdateModel update in TitleUpdates)
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData.Selected = update.Path;
|
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
if (update == SelectedUpdate)
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Selected = update.Path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
||||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
if (!strings.TryGetValue("Language", out string languageName))
|
if (!strings.TryGetValue("Language", out string languageName))
|
||||||
{
|
{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
{
|
{
|
||||||
@@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsScanned { get; set; }
|
public bool IsScanned { get; set; }
|
||||||
public Amiibo.AmiiboApi ScannedAmiibo { get; set; }
|
public AmiiboApi ScannedAmiibo { get; set; }
|
||||||
public AmiiboWindowViewModel ViewModel { get; set; }
|
public AmiiboWindowViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ViewModel.AmiiboSelectedIndex > -1)
|
if (ViewModel.AmiiboSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
||||||
ScannedAmiibo = amiibo;
|
ScannedAmiibo = amiibo;
|
||||||
IsScanned = true;
|
IsScanned = true;
|
||||||
Close();
|
Close();
|
||||||
|
@@ -6,11 +6,8 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Button = Avalonia.Controls.Button;
|
using Button = Avalonia.Controls.Button;
|
||||||
|
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
|
||||||
public enum AntiAliasing
|
public enum AntiAliasing
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@@ -9,4 +13,4 @@
|
|||||||
SmaaHigh,
|
SmaaHigh,
|
||||||
SmaaUltra
|
SmaaUltra
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
|
||||||
public enum AspectRatio
|
public enum AspectRatio
|
||||||
{
|
{
|
||||||
Fixed4x3,
|
Fixed4x3,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
|
||||||
public enum BackendThreading
|
public enum BackendThreading
|
||||||
{
|
{
|
||||||
Auto,
|
Auto,
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(List<DownloadableContentContainer>))]
|
||||||
|
public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||||
public enum GraphicsBackend
|
public enum GraphicsBackend
|
||||||
{
|
{
|
||||||
Vulkan,
|
Vulkan,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))]
|
||||||
public enum GraphicsDebugLevel
|
public enum GraphicsDebugLevel
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))]
|
||||||
public enum GamepadInputId : byte
|
public enum GamepadInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
@@ -51,4 +55,4 @@
|
|||||||
|
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
{
|
{
|
||||||
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
||||||
{
|
{
|
||||||
|
private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
||||||
{
|
{
|
||||||
// Temporary reader to get the backend type
|
// Temporary reader to get the backend type
|
||||||
@@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
|
|
||||||
return motionBackendType switch
|
return motionBackendType switch
|
||||||
{
|
{
|
||||||
MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options),
|
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController),
|
||||||
MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options),
|
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
switch (value.MotionBackend)
|
switch (value.MotionBackend)
|
||||||
{
|
{
|
||||||
case MotionInputBackendType.GamepadDriver:
|
case MotionInputBackendType.GamepadDriver:
|
||||||
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options);
|
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController);
|
||||||
break;
|
break;
|
||||||
case MotionInputBackendType.CemuHook:
|
case MotionInputBackendType.CemuHook:
|
||||||
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options);
|
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(JsonMotionConfigControllerConverter))]
|
||||||
public class MotionConfigController
|
public class MotionConfigController
|
||||||
{
|
{
|
||||||
public MotionInputBackendType MotionBackend { get; set; }
|
public MotionInputBackendType MotionBackend { get; set; }
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(MotionConfigController))]
|
||||||
|
[JsonSerializable(typeof(CemuHookMotionConfigController))]
|
||||||
|
[JsonSerializable(typeof(StandardMotionConfigController))]
|
||||||
|
public partial class MotionConfigJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))]
|
||||||
public enum MotionInputBackendType : byte
|
public enum MotionInputBackendType : byte
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))]
|
||||||
public enum StickInputId : byte
|
public enum StickInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
@@ -8,4 +12,4 @@
|
|||||||
|
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,12 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
[Flags]
|
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
|
[Flags]
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
|
||||||
public enum ControllerType : int
|
public enum ControllerType : int
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
|
||||||
public enum InputBackendType
|
public enum InputBackendType
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(JsonInputConfigConverter))]
|
||||||
public class InputConfig : INotifyPropertyChanged
|
public class InputConfig : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(InputConfig))]
|
||||||
|
[JsonSerializable(typeof(StandardKeyboardInputConfig))]
|
||||||
|
[JsonSerializable(typeof(StandardControllerInputConfig))]
|
||||||
|
public partial class InputConfigJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,16 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
class JsonInputConfigConverter : JsonConverter<InputConfig>
|
public class JsonInputConfigConverter : JsonConverter<InputConfig>
|
||||||
{
|
{
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
||||||
{
|
{
|
||||||
// Temporary reader to get the backend type
|
// Temporary reader to get the backend type
|
||||||
@@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
|
|
||||||
return backendType switch
|
return backendType switch
|
||||||
{
|
{
|
||||||
InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options),
|
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig),
|
||||||
InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options),
|
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
switch (value.Backend)
|
switch (value.Backend)
|
||||||
{
|
{
|
||||||
case InputBackendType.WindowKeyboard:
|
case InputBackendType.WindowKeyboard:
|
||||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options);
|
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig);
|
||||||
break;
|
break;
|
||||||
case InputBackendType.GamepadSDL2:
|
case InputBackendType.GamepadSDL2:
|
||||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options);
|
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<Key>))]
|
||||||
public enum Key
|
public enum Key
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
@@ -136,4 +140,4 @@
|
|||||||
|
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,7 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
// NOTE: Please don't change this to struct.
|
||||||
|
// This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys.
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync { get; set; }
|
public Key ToggleVsync { get; set; }
|
||||||
@@ -12,4 +14,4 @@
|
|||||||
public Key VolumeUp { get; set; }
|
public Key VolumeUp { get; set; }
|
||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,10 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))]
|
||||||
public enum PlayerIndex : int
|
public enum PlayerIndex : int
|
||||||
{
|
{
|
||||||
Player1 = 0,
|
Player1 = 0,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
|
||||||
public enum MemoryManagerMode : byte
|
public enum MemoryManagerMode : byte
|
||||||
{
|
{
|
||||||
SoftwarePageTable,
|
SoftwarePageTable,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))]
|
||||||
public enum ScalingFilter
|
public enum ScalingFilter
|
||||||
{
|
{
|
||||||
Bilinear,
|
Bilinear,
|
||||||
|
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(TitleUpdateMetadata))]
|
||||||
|
public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,22 +1,20 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
internal class DefaultLogFormatter : ILogFormatter
|
internal class DefaultLogFormatter : ILogFormatter
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
public string Format(LogEventArgs args)
|
public string Format(LogEventArgs args)
|
||||||
{
|
{
|
||||||
StringBuilder sb = _stringBuilderPool.Allocate();
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sb.Clear();
|
sb.Clear();
|
||||||
|
|
||||||
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time);
|
sb.Append($@"{args.Time:hh\:mm\:ss\.fff}");
|
||||||
sb.Append($" |{args.Level.ToString()[0]}| ");
|
sb.Append($" |{args.Level.ToString()[0]}| ");
|
||||||
|
|
||||||
if (args.ThreadName != null)
|
if (args.ThreadName != null)
|
||||||
@@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
sb.Append(args.Message);
|
sb.Append(args.Message);
|
||||||
|
|
||||||
if (args.Data != null)
|
if (args.Data is not null)
|
||||||
{
|
{
|
||||||
PropertyInfo[] props = args.Data.GetType().GetProperties();
|
sb.Append(' ');
|
||||||
|
DynamicObjectFormatter.Format(sb, args.Data);
|
||||||
sb.Append(" {");
|
|
||||||
|
|
||||||
foreach (var prop in props)
|
|
||||||
{
|
|
||||||
sb.Append(prop.Name);
|
|
||||||
sb.Append(": ");
|
|
||||||
|
|
||||||
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
|
||||||
{
|
|
||||||
Array array = (Array)prop.GetValue(args.Data);
|
|
||||||
foreach (var item in array)
|
|
||||||
{
|
|
||||||
sb.Append(item.ToString());
|
|
||||||
sb.Append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 2, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append(prop.GetValue(args.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(" ; ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We remove the final ';' from the string
|
|
||||||
if (props.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 3, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_stringBuilderPool.Release(sb);
|
StringBuilderPool.Release(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
internal class DynamicObjectFormatter
|
||||||
|
{
|
||||||
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
|
public static string? Format(object? dynamicObject)
|
||||||
|
{
|
||||||
|
if (dynamicObject is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Format(sb, dynamicObject);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
StringBuilderPool.Release(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Format(StringBuilder sb, object? dynamicObject)
|
||||||
|
{
|
||||||
|
if (dynamicObject is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyInfo[] props = dynamicObject.GetType().GetProperties();
|
||||||
|
|
||||||
|
sb.Append('{');
|
||||||
|
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
sb.Append(prop.Name);
|
||||||
|
sb.Append(": ");
|
||||||
|
|
||||||
|
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
||||||
|
{
|
||||||
|
Array? array = (Array?) prop.GetValue(dynamicObject);
|
||||||
|
|
||||||
|
if (array is not null)
|
||||||
|
{
|
||||||
|
foreach (var item in array)
|
||||||
|
{
|
||||||
|
sb.Append(item);
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Remove(sb.Length - 2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(prop.GetValue(dynamicObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(" ; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We remove the final ';' from the string
|
||||||
|
if (props.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Remove(sb.Length - 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<LogClass>))]
|
||||||
public enum LogClass
|
public enum LogClass
|
||||||
{
|
{
|
||||||
Application,
|
Application,
|
||||||
|
@@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
public readonly string Message;
|
public readonly string Message;
|
||||||
public readonly object Data;
|
public readonly object Data;
|
||||||
|
|
||||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message)
|
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null)
|
||||||
{
|
|
||||||
Level = level;
|
|
||||||
Time = time;
|
|
||||||
ThreadName = threadName;
|
|
||||||
Message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data)
|
|
||||||
{
|
{
|
||||||
Level = level;
|
Level = level;
|
||||||
Time = time;
|
Time = time;
|
||||||
|
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
internal class LogEventArgsJson
|
||||||
|
{
|
||||||
|
public LogLevel Level { get; }
|
||||||
|
public TimeSpan Time { get; }
|
||||||
|
public string ThreadName { get; }
|
||||||
|
|
||||||
|
public string Message { get; }
|
||||||
|
public string Data { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null)
|
||||||
|
{
|
||||||
|
Level = level;
|
||||||
|
Time = time;
|
||||||
|
ThreadName = threadName;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogEventArgsJson FromLogEventArgs(LogEventArgs args)
|
||||||
|
{
|
||||||
|
return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(LogEventArgsJson))]
|
||||||
|
internal partial class LogEventJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))]
|
||||||
public enum LogLevel
|
public enum LogLevel
|
||||||
{
|
{
|
||||||
Debug,
|
Debug,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
using System.IO;
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
@@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
public void Log(object sender, LogEventArgs e)
|
public void Log(object sender, LogEventArgs e)
|
||||||
{
|
{
|
||||||
string text = JsonSerializer.Serialize(e);
|
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
||||||
|
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
||||||
using (BinaryWriter writer = new BinaryWriter(_stream))
|
|
||||||
{
|
|
||||||
writer.Write(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")]
|
||||||
|
[JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")]
|
||||||
|
public partial class CommonJsonContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,62 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using System.IO;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
public class JsonHelper
|
public class JsonHelper
|
||||||
{
|
{
|
||||||
public static JsonNamingPolicy SnakeCase { get; }
|
private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy();
|
||||||
|
private const int DefaultFileWriteBufferSize = 4096;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new serializer options with default settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
|
||||||
|
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
|
||||||
|
/// </remarks>
|
||||||
|
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
|
||||||
|
{
|
||||||
|
JsonSerializerOptions options = new()
|
||||||
|
{
|
||||||
|
DictionaryKeyPolicy = SnakeCasePolicy,
|
||||||
|
PropertyNamingPolicy = SnakeCasePolicy,
|
||||||
|
WriteIndented = indented,
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize(value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough);
|
||||||
|
JsonSerializer.Serialize(file, value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
using FileStream file = File.OpenRead(filePath);
|
||||||
|
return JsonSerializer.Deserialize(file, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
JsonSerializer.Serialize(stream, value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
||||||
{
|
{
|
||||||
@@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
for (int i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.Append("_");
|
builder.Append('_');
|
||||||
builder.Append(char.ToLowerInvariant(c));
|
builder.Append(char.ToLowerInvariant(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonHelper()
|
|
||||||
{
|
|
||||||
SnakeCase = new SnakeCaseNamingPolicy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
JsonSerializerOptions options = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
DictionaryKeyPolicy = SnakeCase,
|
|
||||||
PropertyNamingPolicy = SnakeCase,
|
|
||||||
WriteIndented = prettyPrint,
|
|
||||||
AllowTrailingCommas = true,
|
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip
|
|
||||||
};
|
|
||||||
|
|
||||||
options.Converters.Add(new JsonStringEnumConverter());
|
|
||||||
options.Converters.Add(new JsonInputConfigConverter());
|
|
||||||
options.Converters.Add(new JsonMotionConfigControllerConverter());
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(Stream stream)
|
|
||||||
{
|
|
||||||
using (BinaryReader reader = new BinaryReader(stream))
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T DeserializeFromFile<T>(string path)
|
|
||||||
{
|
|
||||||
return Deserialize<T>(File.ReadAllText(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(string json)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
|
||||||
{
|
|
||||||
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
34
Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
34
Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>.
|
||||||
|
/// Get rid of this converter if dotnet supports similar functionality out of the box.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="TEnum">Type of enum to serialize</typeparam>
|
||||||
|
public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
|
||||||
|
{
|
||||||
|
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var enumValue = reader.GetString();
|
||||||
|
if (string.IsNullOrEmpty(enumValue))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enum.Parse<TEnum>(enumValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
Src1AlphaGl = 0xc902,
|
Src1AlphaGl = 0xc902,
|
||||||
OneMinusSrc1AlphaGl = 0xc903
|
OneMinusSrc1AlphaGl = 0xc903
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BlendFactorExtensions
|
||||||
|
{
|
||||||
|
public static bool IsDualSource(this BlendFactor factor)
|
||||||
|
{
|
||||||
|
switch (factor)
|
||||||
|
{
|
||||||
|
case BlendFactor.Src1Color:
|
||||||
|
case BlendFactor.Src1ColorGl:
|
||||||
|
case BlendFactor.Src1Alpha:
|
||||||
|
case BlendFactor.Src1AlphaGl:
|
||||||
|
case BlendFactor.OneMinusSrc1Color:
|
||||||
|
case BlendFactor.OneMinusSrc1ColorGl:
|
||||||
|
case BlendFactor.OneMinusSrc1Alpha:
|
||||||
|
case BlendFactor.OneMinusSrc1AlphaGl:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
Signal();
|
Signal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the dual-source blend enabled state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enabled">True if blending is enabled and using dual-source blend</param>
|
||||||
|
public void SetDualSourceBlendEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled != _graphics.DualSourceBlendEnable)
|
||||||
|
{
|
||||||
|
_graphics.DualSourceBlendEnable = enabled;
|
||||||
|
|
||||||
|
Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
bool blendIndependent = _state.State.BlendIndependent;
|
bool blendIndependent = _state.State.BlendIndependent;
|
||||||
ColorF blendConstant = _state.State.BlendConstant;
|
ColorF blendConstant = _state.State.BlendConstant;
|
||||||
|
|
||||||
|
bool dualSourceBlendEnabled = false;
|
||||||
|
|
||||||
if (blendIndependent)
|
if (blendIndependent)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
@@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
FilterBlendFactor(blend.AlphaSrcFactor, index),
|
FilterBlendFactor(blend.AlphaSrcFactor, index),
|
||||||
FilterBlendFactor(blend.AlphaDstFactor, index));
|
FilterBlendFactor(blend.AlphaDstFactor, index));
|
||||||
|
|
||||||
|
if (enable &&
|
||||||
|
(blend.ColorSrcFactor.IsDualSource() ||
|
||||||
|
blend.ColorDstFactor.IsDualSource() ||
|
||||||
|
blend.AlphaSrcFactor.IsDualSource() ||
|
||||||
|
blend.AlphaDstFactor.IsDualSource()))
|
||||||
|
{
|
||||||
|
dualSourceBlendEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
_pipeline.BlendDescriptors[index] = descriptor;
|
_pipeline.BlendDescriptors[index] = descriptor;
|
||||||
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
||||||
}
|
}
|
||||||
@@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
FilterBlendFactor(blend.AlphaSrcFactor, 0),
|
FilterBlendFactor(blend.AlphaSrcFactor, 0),
|
||||||
FilterBlendFactor(blend.AlphaDstFactor, 0));
|
FilterBlendFactor(blend.AlphaDstFactor, 0));
|
||||||
|
|
||||||
|
if (enable &&
|
||||||
|
(blend.ColorSrcFactor.IsDualSource() ||
|
||||||
|
blend.ColorDstFactor.IsDualSource() ||
|
||||||
|
blend.AlphaSrcFactor.IsDualSource() ||
|
||||||
|
blend.AlphaDstFactor.IsDualSource()))
|
||||||
|
{
|
||||||
|
dualSourceBlendEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
{
|
{
|
||||||
_pipeline.BlendDescriptors[index] = descriptor;
|
_pipeline.BlendDescriptors[index] = descriptor;
|
||||||
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
|
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryDualSourceBlendEnable()
|
||||||
|
{
|
||||||
|
return _oldSpecState.GraphicsState.DualSourceBlendEnable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public InputTopology QueryPrimitiveTopology()
|
public InputTopology QueryPrimitiveTopology()
|
||||||
{
|
{
|
||||||
|
@@ -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 = 4368;
|
private const uint CodeGenVersion = 4404;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
|
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryDualSourceBlendEnable()
|
||||||
|
{
|
||||||
|
return _state.GraphicsState.DualSourceBlendEnable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public InputTopology QueryPrimitiveTopology()
|
public InputTopology QueryPrimitiveTopology()
|
||||||
{
|
{
|
||||||
|
@@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Array8<AttributeType> FragmentOutputTypes;
|
public Array8<AttributeType> FragmentOutputTypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether dual source blend is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool DualSourceBlendEnable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU graphics state.
|
/// Creates a new GPU graphics state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
||||||
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
|
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
|
||||||
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
|
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
|
||||||
|
/// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param>
|
||||||
public GpuChannelGraphicsState(
|
public GpuChannelGraphicsState(
|
||||||
bool earlyZForce,
|
bool earlyZForce,
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
@@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
ref Array32<AttributeType> attributeTypes,
|
ref Array32<AttributeType> attributeTypes,
|
||||||
bool hasConstantBufferDrawParameters,
|
bool hasConstantBufferDrawParameters,
|
||||||
bool hasUnalignedStorageBuffer,
|
bool hasUnalignedStorageBuffer,
|
||||||
ref Array8<AttributeType> fragmentOutputTypes)
|
ref Array8<AttributeType> fragmentOutputTypes,
|
||||||
|
bool dualSourceBlendEnable)
|
||||||
{
|
{
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
Topology = topology;
|
Topology = topology;
|
||||||
@@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
||||||
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
||||||
FragmentOutputTypes = fragmentOutputTypes;
|
FragmentOutputTypes = fragmentOutputTypes;
|
||||||
|
DualSourceBlendEnable = dualSourceBlendEnable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Matches(channel, ref poolState, checkTextures, isCompute: false);
|
return Matches(channel, ref poolState, checkTextures, isCompute: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
|
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
|
||||||
(BlendingFactorDest)blend.AlphaDstFactor.Convert());
|
(BlendingFactorDest)blend.AlphaDstFactor.Convert());
|
||||||
|
|
||||||
static bool IsDualSource(BlendFactor factor)
|
|
||||||
{
|
|
||||||
switch (factor)
|
|
||||||
{
|
|
||||||
case BlendFactor.Src1Color:
|
|
||||||
case BlendFactor.Src1ColorGl:
|
|
||||||
case BlendFactor.Src1Alpha:
|
|
||||||
case BlendFactor.Src1AlphaGl:
|
|
||||||
case BlendFactor.OneMinusSrc1Color:
|
|
||||||
case BlendFactor.OneMinusSrc1ColorGl:
|
|
||||||
case BlendFactor.OneMinusSrc1Alpha:
|
|
||||||
case BlendFactor.OneMinusSrc1AlphaGl:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureFramebuffer();
|
EnsureFramebuffer();
|
||||||
|
|
||||||
_framebuffer.SetDualSourceBlend(
|
_framebuffer.SetDualSourceBlend(
|
||||||
IsDualSource(blend.ColorSrcFactor) ||
|
blend.ColorSrcFactor.IsDualSource() ||
|
||||||
IsDualSource(blend.ColorDstFactor) ||
|
blend.ColorDstFactor.IsDualSource() ||
|
||||||
IsDualSource(blend.AlphaSrcFactor) ||
|
blend.AlphaSrcFactor.IsDualSource() ||
|
||||||
IsDualSource(blend.AlphaDstFactor));
|
blend.AlphaDstFactor.IsDualSource());
|
||||||
|
|
||||||
if (_blendConstant != blend.BlendConstant)
|
if (_blendConstant != blend.BlendConstant)
|
||||||
{
|
{
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int usedAttributes = context.Config.UsedOutputAttributes;
|
int usedAttributes = context.Config.UsedOutputAttributes;
|
||||||
|
|
||||||
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
||||||
|
{
|
||||||
|
int firstOutput = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
int mask = 3 << firstOutput;
|
||||||
|
|
||||||
|
if ((usedAttributes & mask) == mask)
|
||||||
|
{
|
||||||
|
usedAttributes &= ~mask;
|
||||||
|
DeclareOutputDualSourceBlendAttribute(context, firstOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (usedAttributes != 0)
|
while (usedAttributes != 0)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
@@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr)
|
||||||
|
{
|
||||||
|
string name = $"{DefaultNames.OAttributePrefix}{attr}";
|
||||||
|
string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}";
|
||||||
|
|
||||||
|
context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};");
|
||||||
|
context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
|
||||||
|
}
|
||||||
|
|
||||||
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())
|
||||||
|
@@ -6,6 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
@@ -622,7 +623,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
||||||
{
|
{
|
||||||
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
|
||||||
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
||||||
|
{
|
||||||
|
int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
|
||||||
|
int index = location - firstLocation;
|
||||||
|
int mask = 3 << firstLocation;
|
||||||
|
|
||||||
|
if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
|
||||||
|
{
|
||||||
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
|
||||||
|
context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOutAttr)
|
if (!isOutAttr)
|
||||||
|
@@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries dual source blend state.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if blending is enabled with a dual source blend equation, false otherwise</returns>
|
||||||
|
bool QueryDualSourceBlendEnable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -9,21 +9,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
|
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
private readonly Vk _api;
|
private readonly Vk _api;
|
||||||
private readonly PhysicalDevice _physicalDevice;
|
private readonly VulkanPhysicalDevice _physicalDevice;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
|
|
||||||
|
|
||||||
public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount)
|
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_physicalDevice = physicalDevice;
|
_physicalDevice = physicalDevice;
|
||||||
_device = device;
|
_device = device;
|
||||||
_blockLists = new List<MemoryAllocatorBlockList>();
|
_blockLists = new List<MemoryAllocatorBlockList>();
|
||||||
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
|
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)_physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
||||||
|
|
||||||
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
@@ -64,9 +61,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
uint memoryTypeBits,
|
uint memoryTypeBits,
|
||||||
MemoryPropertyFlags flags)
|
MemoryPropertyFlags flags)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
||||||
{
|
{
|
||||||
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
|
var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i];
|
||||||
|
|
||||||
if ((memoryTypeBits & (1 << i)) != 0)
|
if ((memoryTypeBits & (1 << i)) != 0)
|
||||||
{
|
{
|
||||||
@@ -80,15 +77,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice)
|
public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice)
|
||||||
{
|
{
|
||||||
// The device is regarded as having shared memory if all heaps have the device local bit.
|
for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++)
|
||||||
|
|
||||||
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
|
|
||||||
|
|
||||||
for (int i = 0; i < properties.MemoryHeapCount; i++)
|
|
||||||
{
|
{
|
||||||
if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
|
if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
153
Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs
Normal file
153
Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
class VulkanDebugMessenger : IDisposable
|
||||||
|
{
|
||||||
|
private static string[] _excludedMessages = new string[]
|
||||||
|
{
|
||||||
|
// NOTE: Done on purpose right now.
|
||||||
|
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
||||||
|
// TODO: Figure out if fixable
|
||||||
|
"VUID-vkCmdDrawIndexed-None-04584",
|
||||||
|
// TODO: Might be worth looking into making this happy to possibly optimize copies.
|
||||||
|
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
|
||||||
|
// TODO: Fix this, it's causing too much noise right now.
|
||||||
|
"VUID-VkSubpassDependency-srcSubpass-00867"
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly Vk _api;
|
||||||
|
private readonly Instance _instance;
|
||||||
|
private readonly GraphicsDebugLevel _logLevel;
|
||||||
|
private readonly ExtDebugUtils _debugUtils;
|
||||||
|
private readonly DebugUtilsMessengerEXT? _debugUtilsMessenger;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public VulkanDebugMessenger(Vk api, Instance instance, GraphicsDebugLevel logLevel)
|
||||||
|
{
|
||||||
|
_api = api;
|
||||||
|
_instance = instance;
|
||||||
|
_logLevel = logLevel;
|
||||||
|
|
||||||
|
_api.TryGetInstanceExtension(instance, out _debugUtils);
|
||||||
|
|
||||||
|
Result result = TryInitialize(out _debugUtilsMessenger);
|
||||||
|
|
||||||
|
if (result != Result.Success)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, $"Vulkan debug messenger initialization failed with error {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle)
|
||||||
|
{
|
||||||
|
debugUtilsMessengerHandle = null;
|
||||||
|
|
||||||
|
if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None)
|
||||||
|
{
|
||||||
|
var messageType = _logLevel switch
|
||||||
|
{
|
||||||
|
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
||||||
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
||||||
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
||||||
|
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
||||||
|
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
||||||
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
||||||
|
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageSeverity = _logLevel switch
|
||||||
|
{
|
||||||
|
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
||||||
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
||||||
|
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
||||||
|
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
|
||||||
|
};
|
||||||
|
|
||||||
|
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
||||||
|
MessageType = messageType,
|
||||||
|
MessageSeverity = messageSeverity
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
debugUtilsMessengerCreateInfo.PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(UserCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugUtilsMessengerEXT messengerHandle = default;
|
||||||
|
|
||||||
|
Result result = _debugUtils.CreateDebugUtilsMessenger(_instance, SpanHelpers.AsReadOnlySpan(ref debugUtilsMessengerCreateInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref messengerHandle));
|
||||||
|
|
||||||
|
if (result == Result.Success)
|
||||||
|
{
|
||||||
|
debugUtilsMessengerHandle = messengerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static uint UserCallback(
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
||||||
|
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
||||||
|
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData)
|
||||||
|
{
|
||||||
|
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
||||||
|
|
||||||
|
foreach (string excludedMessagePart in _excludedMessages)
|
||||||
|
{
|
||||||
|
if (msg.Contains(excludedMessagePart))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
||||||
|
{
|
||||||
|
Logger.Debug?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (_debugUtilsMessenger.HasValue)
|
||||||
|
{
|
||||||
|
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger.Value, Span<AllocationCallbacks>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -47,47 +47,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
KhrSwapchain.ExtensionName
|
KhrSwapchain.ExtensionName
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string[] _excludedMessages = new string[]
|
internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions)
|
||||||
{
|
|
||||||
// NOTE: Done on purpose right now.
|
|
||||||
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
|
||||||
// TODO: Figure out if fixable
|
|
||||||
"VUID-vkCmdDrawIndexed-None-04584",
|
|
||||||
// TODO: Might be worth looking into making this happy to possibly optimize copies.
|
|
||||||
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
|
|
||||||
// TODO: Fix this, it's causing too much noise right now.
|
|
||||||
"VUID-VkSubpassDependency-srcSubpass-00867"
|
|
||||||
};
|
|
||||||
|
|
||||||
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger)
|
|
||||||
{
|
{
|
||||||
var enabledLayers = new List<string>();
|
var enabledLayers = new List<string>();
|
||||||
|
|
||||||
|
var instanceExtensions = VulkanInstance.GetInstanceExtensions(api);
|
||||||
|
var instanceLayers = VulkanInstance.GetInstanceLayers(api);
|
||||||
|
|
||||||
void AddAvailableLayer(string layerName)
|
void AddAvailableLayer(string layerName)
|
||||||
{
|
{
|
||||||
uint layerPropertiesCount;
|
if (instanceLayers.Contains(layerName))
|
||||||
|
|
||||||
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
LayerProperties[] layerProperties = new LayerProperties[layerPropertiesCount];
|
|
||||||
|
|
||||||
fixed (LayerProperties* pLayerProperties = layerProperties)
|
|
||||||
{
|
{
|
||||||
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
|
enabledLayers.Add(layerName);
|
||||||
|
}
|
||||||
for (int i = 0; i < layerPropertiesCount; i++)
|
else
|
||||||
{
|
{
|
||||||
string currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
|
Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
|
||||||
|
|
||||||
if (currentLayerName == layerName)
|
|
||||||
{
|
|
||||||
enabledLayers.Add(layerName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logLevel != GraphicsDebugLevel.None)
|
if (logLevel != GraphicsDebugLevel.None)
|
||||||
@@ -95,7 +71,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
var enabledExtensions = requiredExtensions;
|
||||||
|
|
||||||
|
if (instanceExtensions.Contains("VK_EXT_debug_utils"))
|
||||||
|
{
|
||||||
|
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||||
|
|
||||||
@@ -131,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
EnabledLayerCount = (uint)enabledLayers.Count
|
EnabledLayerCount = (uint)enabledLayers.Count
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
|
Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance);
|
||||||
|
|
||||||
Marshal.FreeHGlobal(appName);
|
Marshal.FreeHGlobal(appName);
|
||||||
|
|
||||||
@@ -145,59 +126,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger);
|
result.ThrowOnError();
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe static uint DebugMessenger(
|
internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(Vk api, VulkanInstance instance, SurfaceKHR surface, string preferredGpuId)
|
||||||
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
|
||||||
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
||||||
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
||||||
void* pUserData)
|
|
||||||
{
|
{
|
||||||
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
instance.EnumeratePhysicalDevices(out var physicalDevices).ThrowOnError();
|
||||||
|
|
||||||
foreach (string excludedMessagePart in _excludedMessages)
|
|
||||||
{
|
|
||||||
if (msg.Contains(excludedMessagePart))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
|
||||||
{
|
|
||||||
Logger.Info?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
|
||||||
{
|
|
||||||
Logger.Debug?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId)
|
|
||||||
{
|
|
||||||
uint physicalDeviceCount;
|
|
||||||
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
|
|
||||||
|
|
||||||
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
|
|
||||||
{
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// First we try to pick the the user preferred GPU.
|
// First we try to pick the the user preferred GPU.
|
||||||
for (int i = 0; i < physicalDevices.Length; i++)
|
for (int i = 0; i < physicalDevices.Length; i++)
|
||||||
@@ -243,76 +179,41 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
EnabledLayerCount = 0
|
EnabledLayerCount = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
|
Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance);
|
||||||
|
|
||||||
// We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
|
|
||||||
// If the instance doesn't support it, no device is going to be 1.1 compatible.
|
|
||||||
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
api.DestroyInstance(instance, null);
|
|
||||||
|
|
||||||
return Array.Empty<DeviceInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We currently assume that the instance is compatible with Vulkan 1.2
|
|
||||||
// TODO: Remove this once we relax our initialization codepaths.
|
|
||||||
uint instanceApiVerison = 0;
|
|
||||||
api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
|
|
||||||
|
|
||||||
if (instanceApiVerison < MinimalInstanceVulkanVersion)
|
|
||||||
{
|
|
||||||
api.DestroyInstance(instance, null);
|
|
||||||
|
|
||||||
return Array.Empty<DeviceInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Marshal.FreeHGlobal(appName);
|
Marshal.FreeHGlobal(appName);
|
||||||
|
|
||||||
uint physicalDeviceCount;
|
result.ThrowOnError();
|
||||||
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
|
using VulkanInstance instance = rawInstance;
|
||||||
|
|
||||||
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
|
// We currently assume that the instance is compatible with Vulkan 1.2
|
||||||
|
// TODO: Remove this once we relax our initialization codepaths.
|
||||||
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
|
if (instance.InstanceVersion < MinimalInstanceVulkanVersion)
|
||||||
{
|
{
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
|
return Array.Empty<DeviceInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceInfo[] devices = new DeviceInfo[physicalDevices.Length];
|
instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError();
|
||||||
|
|
||||||
for (int i = 0; i < physicalDevices.Length; i++)
|
List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
|
||||||
|
|
||||||
|
foreach (VulkanPhysicalDevice physicalDevice in physicalDevices)
|
||||||
{
|
{
|
||||||
var physicalDevice = physicalDevices[i];
|
if (physicalDevice.PhysicalDeviceProperties.ApiVersion < MinimalVulkanVersion)
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
|
||||||
|
|
||||||
if (properties.ApiVersion < MinimalVulkanVersion)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
devices[i] = new DeviceInfo(
|
deviceInfos.Add(physicalDevice.ToDeviceInfo());
|
||||||
StringFromIdPair(properties.VendorID, properties.DeviceID),
|
|
||||||
VendorUtils.GetNameFromId(properties.VendorID),
|
|
||||||
Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName),
|
|
||||||
properties.DeviceType == PhysicalDeviceType.DiscreteGpu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api.DestroyInstance(instance, null);
|
return deviceInfos.ToArray();
|
||||||
|
|
||||||
return devices;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string StringFromIdPair(uint vendorId, uint deviceId)
|
private static bool IsPreferredAndSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
|
||||||
{
|
{
|
||||||
return $"0x{vendorId:X}_0x{deviceId:X}";
|
if (physicalDevice.Id != preferredGpuId)
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsPreferredAndSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
|
|
||||||
{
|
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
|
||||||
|
|
||||||
if (StringFromIdPair(properties.VendorID, properties.DeviceID) != preferredGpuId)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -320,68 +221,47 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return IsSuitableDevice(api, physicalDevice, surface);
|
return IsSuitableDevice(api, physicalDevice, surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface)
|
private static bool IsSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface)
|
||||||
{
|
{
|
||||||
int extensionMatches = 0;
|
int extensionMatches = 0;
|
||||||
uint propertiesCount;
|
|
||||||
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
|
foreach (string requiredExtension in _requiredExtensions)
|
||||||
|
|
||||||
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
|
|
||||||
|
|
||||||
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
|
|
||||||
{
|
{
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
|
if (physicalDevice.IsDeviceExtensionPresent(requiredExtension))
|
||||||
|
|
||||||
for (int i = 0; i < propertiesCount; i++)
|
|
||||||
{
|
{
|
||||||
string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
|
extensionMatches++;
|
||||||
|
|
||||||
if (_requiredExtensions.Contains(extensionName))
|
|
||||||
{
|
|
||||||
extensionMatches++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
|
return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
internal static uint FindSuitableQueueFamily(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
||||||
{
|
{
|
||||||
const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
|
const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
|
||||||
|
|
||||||
var khrSurface = new KhrSurface(api.Context);
|
var khrSurface = new KhrSurface(api.Context);
|
||||||
|
|
||||||
uint propertiesCount;
|
for (uint index = 0; index < physicalDevice.QueueFamilyProperties.Length; index++)
|
||||||
|
|
||||||
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
|
|
||||||
|
|
||||||
QueueFamilyProperties[] properties = new QueueFamilyProperties[propertiesCount];
|
|
||||||
|
|
||||||
fixed (QueueFamilyProperties* pProperties = properties)
|
|
||||||
{
|
{
|
||||||
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties);
|
ref QueueFamilyProperties property = ref physicalDevice.QueueFamilyProperties[index];
|
||||||
}
|
|
||||||
|
|
||||||
for (uint index = 0; index < propertiesCount; index++)
|
khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice.PhysicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
|
||||||
{
|
|
||||||
var queueFlags = properties[index].QueueFlags;
|
|
||||||
|
|
||||||
khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
|
if (property.QueueFlags.HasFlag(RequiredFlags) && surfaceSupported)
|
||||||
|
|
||||||
if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
|
|
||||||
{
|
{
|
||||||
queueCount = properties[index].QueueCount;
|
queueCount = property.QueueCount;
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queueCount = 0;
|
queueCount = 0;
|
||||||
|
|
||||||
return InvalidIndex;
|
return InvalidIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Device CreateDevice(Vk api, PhysicalDevice physicalDevice, uint queueFamilyIndex, string[] supportedExtensions, uint queueCount)
|
internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount)
|
||||||
{
|
{
|
||||||
if (queueCount > QueuesCount)
|
if (queueCount > QueuesCount)
|
||||||
{
|
{
|
||||||
@@ -403,8 +283,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PQueuePriorities = queuePriorities
|
PQueuePriorities = queuePriorities
|
||||||
};
|
};
|
||||||
|
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia;
|
||||||
bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia;
|
|
||||||
|
|
||||||
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
|
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
|
||||||
{
|
{
|
||||||
@@ -425,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PNext = features2.PNext
|
PNext = features2.PNext
|
||||||
};
|
};
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
|
||||||
{
|
{
|
||||||
features2.PNext = &supportedFeaturesCustomBorderColor;
|
features2.PNext = &supportedFeaturesCustomBorderColor;
|
||||||
}
|
}
|
||||||
@@ -436,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PNext = features2.PNext
|
PNext = features2.PNext
|
||||||
};
|
};
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
|
||||||
{
|
{
|
||||||
features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart;
|
features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart;
|
||||||
}
|
}
|
||||||
@@ -447,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PNext = features2.PNext
|
PNext = features2.PNext
|
||||||
};
|
};
|
||||||
|
|
||||||
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
|
if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
|
||||||
{
|
{
|
||||||
features2.PNext = &supportedFeaturesTransformFeedback;
|
features2.PNext = &supportedFeaturesTransformFeedback;
|
||||||
}
|
}
|
||||||
@@ -457,14 +336,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
|
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
|
||||||
};
|
};
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
|
||||||
{
|
{
|
||||||
supportedFeaturesRobustness2.PNext = features2.PNext;
|
supportedFeaturesRobustness2.PNext = features2.PNext;
|
||||||
|
|
||||||
features2.PNext = &supportedFeaturesRobustness2;
|
features2.PNext = &supportedFeaturesRobustness2;
|
||||||
}
|
}
|
||||||
|
|
||||||
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2);
|
api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
|
||||||
|
|
||||||
var supportedFeatures = features2.Features;
|
var supportedFeatures = features2.Features;
|
||||||
|
|
||||||
@@ -497,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
|
PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
|
||||||
|
|
||||||
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
|
if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
|
||||||
{
|
{
|
||||||
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
|
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
|
||||||
{
|
{
|
||||||
@@ -511,7 +390,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart;
|
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
|
||||||
{
|
{
|
||||||
featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
|
featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
|
||||||
{
|
{
|
||||||
@@ -526,7 +405,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
|
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
|
||||||
{
|
{
|
||||||
featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
||||||
{
|
{
|
||||||
@@ -542,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt,
|
SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt,
|
||||||
PNext = pExtendedFeatures,
|
PNext = pExtendedFeatures,
|
||||||
ExtendedDynamicState = supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName)
|
ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName)
|
||||||
};
|
};
|
||||||
|
|
||||||
pExtendedFeatures = &featuresExtendedDynamicState;
|
pExtendedFeatures = &featuresExtendedDynamicState;
|
||||||
@@ -560,16 +439,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceVulkan12Features,
|
SType = StructureType.PhysicalDeviceVulkan12Features,
|
||||||
PNext = pExtendedFeatures,
|
PNext = pExtendedFeatures,
|
||||||
DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"),
|
DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"),
|
||||||
DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
|
DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
|
||||||
UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout")
|
UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout")
|
||||||
};
|
};
|
||||||
|
|
||||||
pExtendedFeatures = &featuresVk12;
|
pExtendedFeatures = &featuresVk12;
|
||||||
|
|
||||||
PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8;
|
PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_index_type_uint8"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"))
|
||||||
{
|
{
|
||||||
featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT()
|
featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT()
|
||||||
{
|
{
|
||||||
@@ -583,7 +462,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock;
|
PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"))
|
||||||
{
|
{
|
||||||
featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT()
|
featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT()
|
||||||
{
|
{
|
||||||
@@ -597,7 +476,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
|
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_subgroup_size_control"))
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
|
||||||
{
|
{
|
||||||
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT()
|
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT()
|
||||||
{
|
{
|
||||||
@@ -611,7 +490,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
|
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
|
||||||
supportedFeaturesCustomBorderColor.CustomBorderColors &&
|
supportedFeaturesCustomBorderColor.CustomBorderColors &&
|
||||||
supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
|
supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
|
||||||
{
|
{
|
||||||
@@ -626,7 +505,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pExtendedFeatures = &featuresCustomBorderColor;
|
pExtendedFeatures = &featuresCustomBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray();
|
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
|
||||||
|
|
||||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
||||||
|
|
||||||
@@ -646,7 +525,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PEnabledFeatures = &features
|
PEnabledFeatures = &features
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
|
api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
|
||||||
|
|
||||||
for (int i = 0; i < enabledExtensions.Length; i++)
|
for (int i = 0; i < enabledExtensions.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -655,77 +534,5 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string[] GetSupportedExtensions(Vk api, PhysicalDevice physicalDevice)
|
|
||||||
{
|
|
||||||
uint propertiesCount;
|
|
||||||
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
|
|
||||||
|
|
||||||
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
|
|
||||||
{
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal unsafe static void CreateDebugMessenger(
|
|
||||||
Vk api,
|
|
||||||
GraphicsDebugLevel logLevel,
|
|
||||||
Instance instance,
|
|
||||||
out ExtDebugUtils debugUtils,
|
|
||||||
out DebugUtilsMessengerEXT debugUtilsMessenger)
|
|
||||||
{
|
|
||||||
debugUtils = default;
|
|
||||||
|
|
||||||
if (logLevel != GraphicsDebugLevel.None)
|
|
||||||
{
|
|
||||||
if (!api.TryGetInstanceExtension(instance, out debugUtils))
|
|
||||||
{
|
|
||||||
debugUtilsMessenger = default;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var filterLogType = logLevel switch
|
|
||||||
{
|
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var filterLogSeverity = logLevel switch
|
|
||||||
{
|
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
|
||||||
MessageType = filterLogType,
|
|
||||||
MessageSeverity = filterLogSeverity,
|
|
||||||
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger)
|
|
||||||
};
|
|
||||||
|
|
||||||
debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
debugUtilsMessenger = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
Ryujinx.Graphics.Vulkan/VulkanInstance.cs
Normal file
127
Ryujinx.Graphics.Vulkan/VulkanInstance.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Silk.NET.Core;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
class VulkanInstance : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Vk _api;
|
||||||
|
public readonly Instance Instance;
|
||||||
|
public readonly Version32 InstanceVersion;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
private VulkanInstance(Vk api, Instance instance)
|
||||||
|
{
|
||||||
|
_api = api;
|
||||||
|
Instance = instance;
|
||||||
|
|
||||||
|
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
InstanceVersion = Vk.Version10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint rawInstanceVersion = 0;
|
||||||
|
|
||||||
|
if (api.EnumerateInstanceVersion(ref rawInstanceVersion) != Result.Success)
|
||||||
|
{
|
||||||
|
rawInstanceVersion = Vk.Version11.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceVersion = (Version32)rawInstanceVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result Create(Vk api, ref InstanceCreateInfo createInfo, out VulkanInstance instance)
|
||||||
|
{
|
||||||
|
instance = null;
|
||||||
|
|
||||||
|
Instance rawInstance = default;
|
||||||
|
|
||||||
|
Result result = api.CreateInstance(SpanHelpers.AsReadOnlySpan(ref createInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref rawInstance));
|
||||||
|
|
||||||
|
if (result == Result.Success)
|
||||||
|
{
|
||||||
|
instance = new VulkanInstance(api, rawInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices)
|
||||||
|
{
|
||||||
|
physicalDevices = null;
|
||||||
|
|
||||||
|
uint physicalDeviceCount = 0;
|
||||||
|
|
||||||
|
Result result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), Span<PhysicalDevice>.Empty);
|
||||||
|
|
||||||
|
if (result != Result.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalDevice[] rawPhysicalDevices = new PhysicalDevice[physicalDeviceCount];
|
||||||
|
|
||||||
|
result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), rawPhysicalDevices);
|
||||||
|
|
||||||
|
if (result != Result.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
physicalDevices = rawPhysicalDevices.Select(x => new VulkanPhysicalDevice(_api, x)).ToArray();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IReadOnlySet<string> GetInstanceExtensions(Vk api)
|
||||||
|
{
|
||||||
|
uint propertiesCount = 0;
|
||||||
|
|
||||||
|
api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
|
||||||
|
|
||||||
|
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
|
||||||
|
|
||||||
|
api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IReadOnlySet<string> GetInstanceLayers(Vk api)
|
||||||
|
{
|
||||||
|
uint propertiesCount = 0;
|
||||||
|
|
||||||
|
api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), Span<LayerProperties>.Empty).ThrowOnError();
|
||||||
|
|
||||||
|
LayerProperties[] layerProperties = new LayerProperties[propertiesCount];
|
||||||
|
|
||||||
|
api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), layerProperties).ThrowOnError();
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
return layerProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.LayerName)).ToImmutableHashSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
_api.DestroyInstance(Instance, ReadOnlySpan<AllocationCallbacks>.Empty);
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs
Normal file
70
Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
readonly struct VulkanPhysicalDevice
|
||||||
|
{
|
||||||
|
public readonly PhysicalDevice PhysicalDevice;
|
||||||
|
public readonly PhysicalDeviceFeatures PhysicalDeviceFeatures;
|
||||||
|
public readonly PhysicalDeviceProperties PhysicalDeviceProperties;
|
||||||
|
public readonly PhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties;
|
||||||
|
public readonly QueueFamilyProperties[] QueueFamilyProperties;
|
||||||
|
public readonly string DeviceName;
|
||||||
|
public readonly IReadOnlySet<string> DeviceExtensions;
|
||||||
|
|
||||||
|
public VulkanPhysicalDevice(Vk api, PhysicalDevice physicalDevice)
|
||||||
|
{
|
||||||
|
PhysicalDevice = physicalDevice;
|
||||||
|
PhysicalDeviceFeatures = api.GetPhysicalDeviceFeature(PhysicalDevice);
|
||||||
|
|
||||||
|
api.GetPhysicalDeviceProperties(PhysicalDevice, out var physicalDeviceProperties);
|
||||||
|
PhysicalDeviceProperties = physicalDeviceProperties;
|
||||||
|
|
||||||
|
api.GetPhysicalDeviceMemoryProperties(PhysicalDevice, out PhysicalDeviceMemoryProperties);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
DeviceName = Marshal.PtrToStringAnsi((IntPtr)physicalDeviceProperties.DeviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint propertiesCount = 0;
|
||||||
|
|
||||||
|
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), Span<QueueFamilyProperties>.Empty);
|
||||||
|
|
||||||
|
QueueFamilyProperties = new QueueFamilyProperties[propertiesCount];
|
||||||
|
|
||||||
|
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), QueueFamilyProperties);
|
||||||
|
|
||||||
|
api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
|
||||||
|
|
||||||
|
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
|
||||||
|
|
||||||
|
api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Id => $"0x{PhysicalDeviceProperties.VendorID:X}_0x{PhysicalDeviceProperties.DeviceID:X}";
|
||||||
|
|
||||||
|
public bool IsDeviceExtensionPresent(string extension) => DeviceExtensions.Contains(extension);
|
||||||
|
|
||||||
|
public DeviceInfo ToDeviceInfo()
|
||||||
|
{
|
||||||
|
return new DeviceInfo(
|
||||||
|
Id,
|
||||||
|
VendorUtils.GetNameFromId(PhysicalDeviceProperties.VendorID),
|
||||||
|
DeviceName,
|
||||||
|
PhysicalDeviceProperties.DeviceType == PhysicalDeviceType.DiscreteGpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
public sealed class VulkanRenderer : IRenderer
|
public sealed class VulkanRenderer : IRenderer
|
||||||
{
|
{
|
||||||
private Instance _instance;
|
private VulkanInstance _instance;
|
||||||
private SurfaceKHR _surface;
|
private SurfaceKHR _surface;
|
||||||
private PhysicalDevice _physicalDevice;
|
private VulkanPhysicalDevice _physicalDevice;
|
||||||
private Device _device;
|
private Device _device;
|
||||||
private WindowBase _window;
|
private WindowBase _window;
|
||||||
|
|
||||||
@@ -36,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
||||||
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
||||||
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
||||||
internal ExtDebugUtils DebugUtilsApi { get; private set; }
|
|
||||||
|
|
||||||
internal uint QueueFamilyIndex { get; private set; }
|
internal uint QueueFamilyIndex { get; private set; }
|
||||||
internal Queue Queue { get; private set; }
|
internal Queue Queue { get; private set; }
|
||||||
@@ -57,11 +56,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal HashSet<ITexture> Textures { get; }
|
internal HashSet<ITexture> Textures { get; }
|
||||||
internal HashSet<SamplerHolder> Samplers { get; }
|
internal HashSet<SamplerHolder> Samplers { get; }
|
||||||
|
|
||||||
|
private VulkanDebugMessenger _debugMessenger;
|
||||||
private Counters _counters;
|
private Counters _counters;
|
||||||
private SyncManager _syncManager;
|
private SyncManager _syncManager;
|
||||||
|
|
||||||
private PipelineFull _pipeline;
|
private PipelineFull _pipeline;
|
||||||
private DebugUtilsMessengerEXT _debugUtilsMessenger;
|
|
||||||
|
|
||||||
internal HelperShader HelperShader { get; private set; }
|
internal HelperShader HelperShader { get; private set; }
|
||||||
internal PipelineFull PipelineInternal => _pipeline;
|
internal PipelineFull PipelineInternal => _pipeline;
|
||||||
@@ -107,33 +106,31 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex)
|
private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
|
||||||
{
|
{
|
||||||
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice);
|
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
|
||||||
|
|
||||||
var supportedFeatures = Api.GetPhysicalDeviceFeature(_physicalDevice);
|
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi))
|
||||||
|
|
||||||
if (Api.TryGetDeviceExtension(_instance, _device, out ExtConditionalRendering conditionalRenderingApi))
|
|
||||||
{
|
{
|
||||||
ConditionalRenderingApi = conditionalRenderingApi;
|
ConditionalRenderingApi = conditionalRenderingApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Api.TryGetDeviceExtension(_instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
|
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
|
||||||
{
|
{
|
||||||
ExtendedDynamicStateApi = extendedDynamicStateApi;
|
ExtendedDynamicStateApi = extendedDynamicStateApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Api.TryGetDeviceExtension(_instance, _device, out KhrPushDescriptor pushDescriptorApi))
|
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
|
||||||
{
|
{
|
||||||
PushDescriptorApi = pushDescriptorApi;
|
PushDescriptorApi = pushDescriptorApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Api.TryGetDeviceExtension(_instance, _device, out ExtTransformFeedback transformFeedbackApi))
|
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi))
|
||||||
{
|
{
|
||||||
TransformFeedbackApi = transformFeedbackApi;
|
TransformFeedbackApi = transformFeedbackApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Api.TryGetDeviceExtension(_instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
|
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
|
||||||
{
|
{
|
||||||
DrawIndirectCountApi = drawIndirectCountApi;
|
DrawIndirectCountApi = drawIndirectCountApi;
|
||||||
}
|
}
|
||||||
@@ -155,7 +152,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt
|
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt
|
||||||
};
|
};
|
||||||
|
|
||||||
bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced");
|
bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced");
|
||||||
|
|
||||||
if (supportsBlendOperationAdvanced)
|
if (supportsBlendOperationAdvanced)
|
||||||
{
|
{
|
||||||
@@ -168,14 +165,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
|
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
|
||||||
};
|
};
|
||||||
|
|
||||||
bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control");
|
bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control");
|
||||||
|
|
||||||
if (supportsSubgroupSizeControl)
|
if (supportsSubgroupSizeControl)
|
||||||
{
|
{
|
||||||
properties2.PNext = &propertiesSubgroupSizeControl;
|
properties2.PNext = &propertiesSubgroupSizeControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool supportsTransformFeedback = supportedExtensions.Contains(ExtTransformFeedback.ExtensionName);
|
bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
|
||||||
|
|
||||||
PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT()
|
PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT()
|
||||||
{
|
{
|
||||||
@@ -223,30 +220,30 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
|
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
|
||||||
};
|
};
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
|
if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
|
||||||
{
|
{
|
||||||
features2.PNext = &featuresPrimitiveTopologyListRestart;
|
features2.PNext = &featuresPrimitiveTopologyListRestart;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
|
||||||
{
|
{
|
||||||
featuresRobustness2.PNext = features2.PNext;
|
featuresRobustness2.PNext = features2.PNext;
|
||||||
features2.PNext = &featuresRobustness2;
|
features2.PNext = &featuresRobustness2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_KHR_shader_float16_int8"))
|
if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8"))
|
||||||
{
|
{
|
||||||
featuresShaderInt8.PNext = features2.PNext;
|
featuresShaderInt8.PNext = features2.PNext;
|
||||||
features2.PNext = &featuresShaderInt8;
|
features2.PNext = &featuresShaderInt8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
|
if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
|
||||||
{
|
{
|
||||||
featuresCustomBorderColor.PNext = features2.PNext;
|
featuresCustomBorderColor.PNext = features2.PNext;
|
||||||
features2.PNext = &featuresCustomBorderColor;
|
features2.PNext = &featuresCustomBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset");
|
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
|
||||||
|
|
||||||
if (usePortability)
|
if (usePortability)
|
||||||
{
|
{
|
||||||
@@ -257,8 +254,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &featuresPortabilitySubset;
|
features2.PNext = &featuresPortabilitySubset;
|
||||||
}
|
}
|
||||||
|
|
||||||
Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2);
|
Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2);
|
||||||
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
|
Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
|
||||||
|
|
||||||
var portabilityFlags = PortabilitySubsetFlags.None;
|
var portabilityFlags = PortabilitySubsetFlags.None;
|
||||||
uint vertexBufferAlignment = 1;
|
uint vertexBufferAlignment = 1;
|
||||||
@@ -273,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
|
portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
|
bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
|
||||||
featuresCustomBorderColor.CustomBorderColors &&
|
featuresCustomBorderColor.CustomBorderColors &&
|
||||||
featuresCustomBorderColor.CustomBorderColorWithoutFormat;
|
featuresCustomBorderColor.CustomBorderColorWithoutFormat;
|
||||||
|
|
||||||
@@ -285,30 +282,30 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
properties.Limits.FramebufferStencilSampleCounts;
|
properties.Limits.FramebufferStencilSampleCounts;
|
||||||
|
|
||||||
Capabilities = new HardwareCapabilities(
|
Capabilities = new HardwareCapabilities(
|
||||||
supportedExtensions.Contains("VK_EXT_index_type_uint8"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
|
||||||
supportsCustomBorderColor,
|
supportsCustomBorderColor,
|
||||||
supportsBlendOperationAdvanced,
|
supportsBlendOperationAdvanced,
|
||||||
propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
|
propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
|
||||||
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
|
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
|
||||||
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
|
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
|
||||||
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
|
||||||
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
|
||||||
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
|
||||||
supportsSubgroupSizeControl,
|
supportsSubgroupSizeControl,
|
||||||
featuresShaderInt8.ShaderInt8,
|
featuresShaderInt8.ShaderInt8,
|
||||||
supportedExtensions.Contains("VK_EXT_shader_stencil_export"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
||||||
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
||||||
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
||||||
features2.Features.MultiViewport,
|
features2.Features.MultiViewport,
|
||||||
featuresRobustness2.NullDescriptor || IsMoltenVk,
|
featuresRobustness2.NullDescriptor || IsMoltenVk,
|
||||||
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
|
||||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
||||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
|
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
|
||||||
supportsTransformFeedback,
|
supportsTransformFeedback,
|
||||||
propertiesTransformFeedback.TransformFeedbackQueries,
|
propertiesTransformFeedback.TransformFeedbackQueries,
|
||||||
features2.Features.OcclusionQueryPrecise,
|
features2.Features.OcclusionQueryPrecise,
|
||||||
supportedFeatures.PipelineStatisticsQuery,
|
_physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
|
||||||
supportedFeatures.GeometryShader,
|
_physicalDevice.PhysicalDeviceFeatures.GeometryShader,
|
||||||
propertiesSubgroupSizeControl.MinSubgroupSize,
|
propertiesSubgroupSizeControl.MinSubgroupSize,
|
||||||
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
||||||
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
||||||
@@ -316,9 +313,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
portabilityFlags,
|
portabilityFlags,
|
||||||
vertexBufferAlignment);
|
vertexBufferAlignment);
|
||||||
|
|
||||||
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice);
|
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice);
|
||||||
|
|
||||||
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device);
|
||||||
|
|
||||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||||
|
|
||||||
@@ -345,24 +342,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Api = api;
|
Api = api;
|
||||||
|
|
||||||
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger);
|
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions());
|
||||||
|
_debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel);
|
||||||
|
|
||||||
DebugUtilsApi = debugUtils;
|
if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
|
||||||
|
|
||||||
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
|
|
||||||
{
|
{
|
||||||
SurfaceApi = surfaceApi;
|
SurfaceApi = surfaceApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
_surface = _getSurface(_instance, api);
|
_surface = _getSurface(_instance.Instance, api);
|
||||||
_physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId);
|
_physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId);
|
||||||
|
|
||||||
var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount);
|
var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount);
|
||||||
var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice);
|
|
||||||
|
|
||||||
_device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, supportedExtensions, maxQueueCount);
|
_device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount);
|
||||||
|
|
||||||
if (api.TryGetDeviceExtension(_instance, _device, out KhrSwapchain swapchainApi))
|
if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
|
||||||
{
|
{
|
||||||
SwapchainApi = swapchainApi;
|
SwapchainApi = swapchainApi;
|
||||||
}
|
}
|
||||||
@@ -371,9 +366,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Queue = queue;
|
Queue = queue;
|
||||||
QueueLock = new object();
|
QueueLock = new object();
|
||||||
|
|
||||||
LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex);
|
LoadFeatures(maxQueueCount, queueFamilyIndex);
|
||||||
|
|
||||||
_window = new Window(this, _surface, _physicalDevice, _device);
|
_window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
@@ -538,10 +533,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PNext = &featuresVk12
|
PNext = &featuresVk12
|
||||||
};
|
};
|
||||||
|
|
||||||
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
|
Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
|
||||||
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
|
|
||||||
|
|
||||||
var limits = properties.Limits;
|
var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
|
||||||
|
|
||||||
return new Capabilities(
|
return new Capabilities(
|
||||||
api: TargetApi.Vulkan,
|
api: TargetApi.Vulkan,
|
||||||
@@ -625,7 +619,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private unsafe void PrintGpuInformation()
|
private unsafe void PrintGpuInformation()
|
||||||
{
|
{
|
||||||
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
|
var properties = _physicalDevice.PhysicalDeviceProperties;
|
||||||
|
|
||||||
string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
|
string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
|
||||||
|
|
||||||
@@ -794,11 +788,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
MemoryAllocator.Dispose();
|
MemoryAllocator.Dispose();
|
||||||
|
|
||||||
if (_debugUtilsMessenger.Handle != 0)
|
|
||||||
{
|
|
||||||
DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var shader in Shaders)
|
foreach (var shader in Shaders)
|
||||||
{
|
{
|
||||||
shader.Dispose();
|
shader.Dispose();
|
||||||
@@ -814,12 +803,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
sampler.Dispose();
|
sampler.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceApi.DestroySurface(_instance, _surface, null);
|
SurfaceApi.DestroySurface(_instance.Instance, _surface, null);
|
||||||
|
|
||||||
Api.DestroyDevice(_device, null);
|
Api.DestroyDevice(_device, null);
|
||||||
|
|
||||||
|
_debugMessenger.Dispose();
|
||||||
|
|
||||||
// Last step destroy the instance
|
// Last step destroy the instance
|
||||||
Api.DestroyInstance(_instance, null);
|
_instance.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,11 +1,11 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
{
|
{
|
||||||
@@ -13,29 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
{
|
{
|
||||||
private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
|
private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
|
||||||
|
|
||||||
private struct ProfilesJson
|
private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
{
|
|
||||||
[JsonPropertyName("profiles")]
|
|
||||||
public List<UserProfileJson> Profiles { get; set; }
|
|
||||||
[JsonPropertyName("last_opened")]
|
|
||||||
public string LastOpened { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct UserProfileJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("user_id")]
|
|
||||||
public string UserId { get; set; }
|
|
||||||
[JsonPropertyName("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonPropertyName("account_state")]
|
|
||||||
public AccountState AccountState { get; set; }
|
|
||||||
[JsonPropertyName("online_play_state")]
|
|
||||||
public AccountState OnlinePlayState { get; set; }
|
|
||||||
[JsonPropertyName("last_modified_timestamp")]
|
|
||||||
public long LastModifiedTimestamp { get; set; }
|
|
||||||
[JsonPropertyName("image")]
|
|
||||||
public byte[] Image { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserId LastOpened { get; set; }
|
public UserId LastOpened { get; set; }
|
||||||
|
|
||||||
@@ -47,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
|
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson);
|
||||||
|
|
||||||
foreach (var profile in profilesJson.Profiles)
|
foreach (var profile in profilesJson.Profiles)
|
||||||
{
|
{
|
||||||
@@ -92,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true));
|
JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(ProfilesJson))]
|
||||||
|
internal partial class ProfilesJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AccountState>))]
|
||||||
public enum AccountState
|
public enum AccountState
|
||||||
{
|
{
|
||||||
Closed,
|
Closed,
|
||||||
|
10
Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs
Normal file
10
Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types
|
||||||
|
{
|
||||||
|
internal struct ProfilesJson
|
||||||
|
{
|
||||||
|
public List<UserProfileJson> Profiles { get; set; }
|
||||||
|
public string LastOpened { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types
|
||||||
|
{
|
||||||
|
internal struct UserProfileJson
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public AccountState AccountState { get; set; }
|
||||||
|
public AccountState OnlinePlayState { get; set; }
|
||||||
|
public long LastModifiedTimestamp { get; set; }
|
||||||
|
public byte[] Image { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||||
{
|
{
|
||||||
struct AtomicStorage<T> where T: unmanaged
|
struct AtomicStorage<T> where T: unmanaged, ISampledDataStruct
|
||||||
{
|
{
|
||||||
public ulong SamplingNumber;
|
public ulong SamplingNumber;
|
||||||
public T Object;
|
public T Object;
|
||||||
@@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
|||||||
|
|
||||||
public void SetObject(ref T obj)
|
public void SetObject(ref T obj)
|
||||||
{
|
{
|
||||||
ISampledData samplingProvider = obj as ISampledData;
|
ulong samplingNumber = ISampledDataStruct.GetSamplingNumber(ref obj);
|
||||||
|
|
||||||
Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber);
|
Interlocked.Exchange(ref SamplingNumber, samplingNumber);
|
||||||
|
|
||||||
Thread.MemoryBarrier();
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user