Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
62585755fd | |||
56621615b1 | |||
2099a3e84b | |||
7d26e4ac7b | |||
8d41402fa6 | |||
5af8ce7c38 | |||
77c4291c34 | |||
6e92b7a378 | |||
9b852c7481 | |||
c40c3905e2 | |||
a6cd044f0f | |||
f5a1de6ac5 | |||
2aeb5b00e3 | |||
60ba7b71f2 | |||
7c1d2bbb98 | |||
beacf8c1c8 | |||
0dbe45ae37 | |||
2b50e52e48 | |||
49eadbc209 | |||
2df16ded9b | |||
e43390c723 | |||
5af1327068 | |||
88a8d1e567 | |||
bf77d1cab9 |
44
ARMeilleure/Decoders/OpCode32SimdCvtTB.cs
Normal file
44
ARMeilleure/Decoders/OpCode32SimdCvtTB.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCode32SimdCvtTB : OpCode32, IOpCode32Simd
|
||||||
|
{
|
||||||
|
public int Vd { get; }
|
||||||
|
public int Vm { get; }
|
||||||
|
public bool Op { get; } // Convert to Half / Convert from Half
|
||||||
|
public bool T { get; } // Top / Bottom
|
||||||
|
public int Size { get; } // Double / Single
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, false);
|
||||||
|
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, true);
|
||||||
|
|
||||||
|
public OpCode32SimdCvtTB(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
IsThumb = isThumb;
|
||||||
|
|
||||||
|
Op = ((opCode >> 16) & 0x1) != 0;
|
||||||
|
T = ((opCode >> 7) & 0x1) != 0;
|
||||||
|
Size = ((opCode >> 8) & 0x1);
|
||||||
|
|
||||||
|
RegisterSize = Size == 1 ? RegisterSize.Int64 : RegisterSize.Int32;
|
||||||
|
|
||||||
|
if (Size == 1)
|
||||||
|
{
|
||||||
|
if (Op)
|
||||||
|
{
|
||||||
|
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||||
|
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||||
|
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||||
|
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -828,6 +828,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int.
|
SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int.
|
||||||
SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32.
|
SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32.
|
||||||
SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp).
|
SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp).
|
||||||
|
SetVfp("<<<<11101x11001xxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_TB, OpCode32SimdCvtTB.Create, OpCode32SimdCvtTB.CreateT32);
|
||||||
SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||||
SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32);
|
SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32);
|
||||||
SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||||
|
@ -1616,20 +1616,34 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void Frinta_S(ArmEmitterContext context)
|
public static void Frinta_S(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearestAway);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(context, (op1) =>
|
EmitScalarUnaryOpF(context, (op1) =>
|
||||||
{
|
{
|
||||||
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Frinta_V(ArmEmitterContext context)
|
public static void Frinta_V(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearestAway);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpF(context, (op1) =>
|
EmitVectorUnaryOpF(context, (op1) =>
|
||||||
{
|
{
|
||||||
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Frinti_S(ArmEmitterContext context)
|
public static void Frinti_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
@ -3516,9 +3530,18 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss;
|
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss;
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
if ((op.Size & 1) != 0)
|
if ((op.Size & 1) != 0)
|
||||||
{
|
{
|
||||||
@ -3538,9 +3561,18 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps;
|
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps;
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: false);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
|
@ -163,34 +163,76 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void Fcvtas_Gp(ArmEmitterContext context)
|
public static void Fcvtas_Gp(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41Fcvts_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtas_S(ArmEmitterContext context)
|
public static void Fcvtas_S(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true);
|
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtas_V(ArmEmitterContext context)
|
public static void Fcvtas_V(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false);
|
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtau_Gp(ArmEmitterContext context)
|
public static void Fcvtau_Gp(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41Fcvtu_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtau_S(ArmEmitterContext context)
|
public static void Fcvtau_S(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true);
|
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtau_V(ArmEmitterContext context)
|
public static void Fcvtau_V(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false);
|
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtl_V(ArmEmitterContext context)
|
public static void Fcvtl_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
@ -1223,7 +1265,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||||
|
}
|
||||||
|
|
||||||
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
||||||
|
|
||||||
@ -1265,7 +1314,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||||
|
}
|
||||||
|
|
||||||
Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar);
|
Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar);
|
||||||
|
|
||||||
@ -1314,7 +1370,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||||
|
}
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
Operand zero = context.VectorZero();
|
||||||
|
|
||||||
@ -1369,7 +1432,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||||
|
}
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
Operand zero = context.VectorZero();
|
||||||
|
|
||||||
@ -1424,7 +1494,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
|
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
|
||||||
? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes)
|
? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes)
|
||||||
@ -1464,7 +1541,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
|
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
|
||||||
? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes)
|
? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes)
|
||||||
@ -1512,7 +1596,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
Operand zero = context.VectorZero();
|
||||||
|
|
||||||
@ -1567,7 +1658,14 @@ namespace ARMeilleure.Instructions
|
|||||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
|
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
Operand zero = context.VectorZero();
|
||||||
|
|
||||||
|
@ -203,6 +203,9 @@ namespace ARMeilleure.Instructions
|
|||||||
FPRoundingMode roundMode;
|
FPRoundingMode roundMode;
|
||||||
switch (rm)
|
switch (rm)
|
||||||
{
|
{
|
||||||
|
case 0b00:
|
||||||
|
roundMode = FPRoundingMode.ToNearestAway;
|
||||||
|
break;
|
||||||
case 0b01:
|
case 0b01:
|
||||||
roundMode = FPRoundingMode.ToNearest;
|
roundMode = FPRoundingMode.ToNearest;
|
||||||
break;
|
break;
|
||||||
@ -228,7 +231,7 @@ namespace ARMeilleure.Instructions
|
|||||||
bool unsigned = op.Opc == 0;
|
bool unsigned = op.Opc == 0;
|
||||||
int rm = op.Opc2 & 3;
|
int rm = op.Opc2 & 3;
|
||||||
|
|
||||||
if (Optimizations.UseSse41 && rm != 0b00)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
|
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
|
||||||
}
|
}
|
||||||
@ -258,6 +261,74 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Vcvt_TB(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp;
|
||||||
|
|
||||||
|
if (Optimizations.UseF16c)
|
||||||
|
{
|
||||||
|
Debug.Assert(!Optimizations.ForceLegacySse);
|
||||||
|
|
||||||
|
if (op.Op)
|
||||||
|
{
|
||||||
|
Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
|
||||||
|
if (op.Size == 1)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res);
|
||||||
|
}
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
|
||||||
|
res = context.VectorExtract16(res, 0);
|
||||||
|
InsertScalar16(context, op.Vd, op.T, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T));
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res);
|
||||||
|
if (op.Size == 1)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
|
||||||
|
}
|
||||||
|
res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0);
|
||||||
|
InsertScalar(context, op.Vd, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (op.Op)
|
||||||
|
{
|
||||||
|
// Convert to half.
|
||||||
|
|
||||||
|
Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
|
||||||
|
|
||||||
|
MethodInfo method = op.Size == 1
|
||||||
|
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
||||||
|
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
||||||
|
|
||||||
|
context.StoreToContext();
|
||||||
|
Operand res = context.Call(method, src);
|
||||||
|
context.LoadFromContext();
|
||||||
|
|
||||||
|
InsertScalar16(context, op.Vd, op.T, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Convert from half.
|
||||||
|
|
||||||
|
Operand src = ExtractScalar16(context, op.Vm, op.T);
|
||||||
|
|
||||||
|
MethodInfo method = op.Size == 1
|
||||||
|
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
||||||
|
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
||||||
|
|
||||||
|
context.StoreToContext();
|
||||||
|
Operand res = context.Call(method, src);
|
||||||
|
context.LoadFromContext();
|
||||||
|
|
||||||
|
InsertScalar(context, op.Vd, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VRINTA/M/N/P (floating-point).
|
// VRINTA/M/N/P (floating-point).
|
||||||
public static void Vrint_RM(ArmEmitterContext context)
|
public static void Vrint_RM(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
@ -267,15 +338,21 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int rm = op.Opc2 & 3;
|
int rm = op.Opc2 & 3;
|
||||||
|
|
||||||
if (Optimizations.UseSse2 && rm != 0b00)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpSimd32(context, (m) =>
|
EmitScalarUnaryOpSimd32(context, (m) =>
|
||||||
{
|
{
|
||||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
|
||||||
|
|
||||||
FPRoundingMode roundMode = RMToRoundMode(rm);
|
FPRoundingMode roundMode = RMToRoundMode(rm);
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
|
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
||||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
|
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -304,9 +381,19 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
// VRINTA (vector).
|
// VRINTA (vector).
|
||||||
public static void Vrinta_V(ArmEmitterContext context)
|
public static void Vrinta_V(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse41)
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||||
|
{
|
||||||
|
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
|
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VRINTM (vector).
|
// VRINTM (vector).
|
||||||
public static void Vrintm_V(ArmEmitterContext context)
|
public static void Vrintm_V(ArmEmitterContext context)
|
||||||
@ -413,7 +500,14 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
|
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
Operand zero = context.VectorZero();
|
||||||
|
|
||||||
@ -464,7 +558,14 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
|
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||||
|
|
||||||
|
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||||
|
{
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||||
|
}
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
Operand zero = context.VectorZero();
|
||||||
|
|
||||||
|
@ -33,6 +33,14 @@ namespace ARMeilleure.Instructions
|
|||||||
};
|
};
|
||||||
|
|
||||||
public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0;
|
public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0;
|
||||||
|
|
||||||
|
public static ulong X86GetGf2p8LogicalShiftLeft(int shift)
|
||||||
|
{
|
||||||
|
ulong identity = (0b00000001UL << 56) | (0b00000010UL << 48) | (0b00000100UL << 40) | (0b00001000UL << 32) |
|
||||||
|
(0b00010000UL << 24) | (0b00100000UL << 16) | (0b01000000UL << 8) | (0b10000000UL << 0);
|
||||||
|
|
||||||
|
return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "X86 SSE Intrinsics"
|
#region "X86 SSE Intrinsics"
|
||||||
@ -243,19 +251,44 @@ namespace ARMeilleure.Instructions
|
|||||||
throw new ArgumentException($"Invalid rounding mode \"{roundMode}\".");
|
throw new ArgumentException($"Invalid rounding mode \"{roundMode}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong X86GetGf2p8LogicalShiftLeft(int shift)
|
public static Operand EmitSse41RoundToNearestWithTiesToAwayOpF(ArmEmitterContext context, Operand n, bool scalar)
|
||||||
{
|
{
|
||||||
ulong identity =
|
Debug.Assert(n.Type == OperandType.V128);
|
||||||
(0b00000001UL << 56) |
|
|
||||||
(0b00000010UL << 48) |
|
|
||||||
(0b00000100UL << 40) |
|
|
||||||
(0b00001000UL << 32) |
|
|
||||||
(0b00010000UL << 24) |
|
|
||||||
(0b00100000UL << 16) |
|
|
||||||
(0b01000000UL << 8) |
|
|
||||||
(0b10000000UL << 0);
|
|
||||||
|
|
||||||
return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8);
|
Operand nCopy = context.Copy(n);
|
||||||
|
|
||||||
|
Operand rC = Const(X86GetRoundControl(FPRoundingMode.TowardsZero));
|
||||||
|
|
||||||
|
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
|
if ((op.Size & 1) == 0)
|
||||||
|
{
|
||||||
|
Operand signMask = scalar ? X86GetScalar(context, int.MinValue) : X86GetAllElements(context, int.MinValue);
|
||||||
|
signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy);
|
||||||
|
|
||||||
|
// 0x3EFFFFFF == BitConverter.SingleToInt32Bits(0.5f) - 1
|
||||||
|
Operand valueMask = scalar ? X86GetScalar(context, 0x3EFFFFFF) : X86GetAllElements(context, 0x3EFFFFFF);
|
||||||
|
valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask);
|
||||||
|
|
||||||
|
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addss : Intrinsic.X86Addps, nCopy, valueMask);
|
||||||
|
|
||||||
|
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundss : Intrinsic.X86Roundps, nCopy, rC);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand signMask = scalar ? X86GetScalar(context, long.MinValue) : X86GetAllElements(context, long.MinValue);
|
||||||
|
signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy);
|
||||||
|
|
||||||
|
// 0x3FDFFFFFFFFFFFFFL == BitConverter.DoubleToInt64Bits(0.5d) - 1L
|
||||||
|
Operand valueMask = scalar ? X86GetScalar(context, 0x3FDFFFFFFFFFFFFFL) : X86GetAllElements(context, 0x3FDFFFFFFFFFFFFFL);
|
||||||
|
valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask);
|
||||||
|
|
||||||
|
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addsd : Intrinsic.X86Addpd, nCopy, valueMask);
|
||||||
|
|
||||||
|
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundsd : Intrinsic.X86Roundpd, nCopy, rC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
||||||
|
@ -70,6 +70,22 @@ namespace ARMeilleure.Instructions
|
|||||||
context.Copy(vec, insert);
|
context.Copy(vec, insert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Operand ExtractScalar16(ArmEmitterContext context, int reg, bool top)
|
||||||
|
{
|
||||||
|
return context.VectorExtract16(GetVecA32(reg >> 2), ((reg & 3) << 1) | (top ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
|
||||||
|
{
|
||||||
|
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32);
|
||||||
|
|
||||||
|
Operand vec, insert;
|
||||||
|
vec = GetVecA32(reg >> 2);
|
||||||
|
insert = context.VectorInsert16(vec, value, ((reg & 3) << 1) | (top ? 1 : 0));
|
||||||
|
|
||||||
|
context.Copy(vec, insert);
|
||||||
|
}
|
||||||
|
|
||||||
public static Operand ExtractElement(ArmEmitterContext context, int reg, int size, bool signed)
|
public static Operand ExtractElement(ArmEmitterContext context, int reg, int size, bool signed)
|
||||||
{
|
{
|
||||||
return EmitVectorExtract32(context, reg >> (4 - size), reg & ((16 >> size) - 1), size, signed);
|
return EmitVectorExtract32(context, reg >> (4 - size), reg & ((16 >> size) - 1), size, signed);
|
||||||
|
@ -2,9 +2,10 @@ namespace ARMeilleure.State
|
|||||||
{
|
{
|
||||||
public enum FPRoundingMode
|
public enum FPRoundingMode
|
||||||
{
|
{
|
||||||
ToNearest = 0,
|
ToNearest = 0, // With ties to even.
|
||||||
TowardsPlusInfinity = 1,
|
TowardsPlusInfinity = 1,
|
||||||
TowardsMinusInfinity = 2,
|
TowardsMinusInfinity = 2,
|
||||||
TowardsZero = 3
|
TowardsZero = 3,
|
||||||
|
ToNearestAway = 4 // With ties to away.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,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 = 3710; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 3713; //! 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";
|
||||||
|
@ -455,13 +455,16 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
|
||||||
ClearRejitQueue(allowRequeue: true);
|
|
||||||
|
|
||||||
ulong[] overlapAddresses = Array.Empty<ulong>();
|
ulong[] overlapAddresses = Array.Empty<ulong>();
|
||||||
|
|
||||||
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
|
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
|
||||||
|
|
||||||
|
if (overlapsCount != 0)
|
||||||
|
{
|
||||||
|
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
||||||
|
ClearRejitQueue(allowRequeue: true);
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < overlapsCount; index++)
|
for (int index = 0; index < overlapsCount; index++)
|
||||||
{
|
{
|
||||||
ulong overlapAddress = overlapAddresses[index];
|
ulong overlapAddress = overlapAddresses[index];
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||||
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -588,5 +588,9 @@
|
|||||||
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
||||||
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
|
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
|
||||||
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
|
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
|
||||||
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?"
|
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?",
|
||||||
|
"RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?",
|
||||||
|
"SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:",
|
||||||
|
"SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:",
|
||||||
|
"VolumeShort": "Głoś"
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||||
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray());
|
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows())
|
if (!OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
|
@ -295,8 +295,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
if (_validTzRegions.Contains(location))
|
if (_validTzRegions.Contains(location))
|
||||||
{
|
{
|
||||||
TimeZone = location;
|
TimeZone = location;
|
||||||
|
|
||||||
OnPropertyChanged(nameof(TimeZone));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{ReflectionBinding ChangeLanguage}"
|
Command="{ReflectionBinding ChangeLanguage}"
|
||||||
CommandParameter="pl_PL"
|
CommandParameter="pl_PL"
|
||||||
Header="Polish" />
|
Header="Polski" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{ReflectionBinding ChangeLanguage}"
|
Command="{ReflectionBinding ChangeLanguage}"
|
||||||
CommandParameter="ru_RU"
|
CommandParameter="ru_RU"
|
||||||
|
@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Load();
|
Load();
|
||||||
|
|
||||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
|
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
|
||||||
MultiBinding tzMultiBinding = new() { Converter = converter };
|
MultiBinding tzMultiBinding = new() { Converter = converter };
|
||||||
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
||||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||||
|
89
Ryujinx.Common/Memory/SpanOrArray.cs
Normal file
89
Ryujinx.Common/Memory/SpanOrArray.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A struct that can represent both a Span and Array.
|
||||||
|
/// This is useful to keep the Array representation when possible to avoid copies.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Element Type</typeparam>
|
||||||
|
public ref struct SpanOrArray<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public readonly T[] Array;
|
||||||
|
public readonly ReadOnlySpan<T> Span;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new SpanOrArray from an array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Array to store</param>
|
||||||
|
public SpanOrArray(T[] array)
|
||||||
|
{
|
||||||
|
Array = array;
|
||||||
|
Span = ReadOnlySpan<T>.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new SpanOrArray from a readonly span.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Span to store</param>
|
||||||
|
public SpanOrArray(ReadOnlySpan<T> span)
|
||||||
|
{
|
||||||
|
Array = null;
|
||||||
|
Span = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the contained array, or convert the span if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An array containing the data</returns>
|
||||||
|
public T[] ToArray()
|
||||||
|
{
|
||||||
|
return Array ?? Span.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a ReadOnlySpan from either the array or ReadOnlySpan.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A ReadOnlySpan containing the data</returns>
|
||||||
|
public ReadOnlySpan<T> AsSpan()
|
||||||
|
{
|
||||||
|
return Array ?? Span;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cast an array to a SpanOrArray.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Source array</param>
|
||||||
|
public static implicit operator SpanOrArray<T>(T[] array)
|
||||||
|
{
|
||||||
|
return new SpanOrArray<T>(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cast a ReadOnlySpan to a SpanOrArray.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">Source ReadOnlySpan</param>
|
||||||
|
public static implicit operator SpanOrArray<T>(ReadOnlySpan<T> span)
|
||||||
|
{
|
||||||
|
return new SpanOrArray<T>(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cast a Span to a SpanOrArray.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">Source Span</param>
|
||||||
|
public static implicit operator SpanOrArray<T>(Span<T> span)
|
||||||
|
{
|
||||||
|
return new SpanOrArray<T>(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cast a SpanOrArray to a ReadOnlySpan
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="spanOrArray">Source SpanOrArray</param>
|
||||||
|
public static implicit operator ReadOnlySpan<T>(SpanOrArray<T> spanOrArray)
|
||||||
|
{
|
||||||
|
return spanOrArray.AsSpan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -129,7 +129,7 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
private static (Assembly, string) ResolveManifestPath(string filename)
|
private static (Assembly, string) ResolveManifestPath(string filename)
|
||||||
{
|
{
|
||||||
var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
if (segments.Length >= 2)
|
if (segments.Length >= 2)
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
ReadOnlySpan<byte> GetData();
|
ReadOnlySpan<byte> GetData();
|
||||||
ReadOnlySpan<byte> GetData(int layer, int level);
|
ReadOnlySpan<byte> GetData(int layer, int level);
|
||||||
|
|
||||||
void SetData(ReadOnlySpan<byte> data);
|
void SetData(SpanOrArray<byte> data);
|
||||||
void SetData(ReadOnlySpan<byte> data, int layer, int level);
|
void SetData(SpanOrArray<byte> data, int layer, int level);
|
||||||
void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region);
|
void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region);
|
||||||
void SetStorage(BufferRange buffer);
|
void SetStorage(BufferRange buffer);
|
||||||
void Release();
|
void Release();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -107,19 +108,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(SpanOrArray<byte> data)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));
|
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level);
|
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region);
|
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
|
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data;
|
byte[] data;
|
||||||
if (srcLinear)
|
if (srcLinear)
|
||||||
{
|
{
|
||||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
@ -136,11 +137,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public LinkedListNode<Texture> CacheNode { get; set; }
|
public LinkedListNode<Texture> CacheNode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event to fire when texture data is disposed.
|
|
||||||
/// </summary>
|
|
||||||
public event Action<Texture> Disposed;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical memory ranges where the texture data is located.
|
/// Physical memory ranges where the texture data is located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -720,9 +716,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = ConvertToHostCompatibleFormat(data);
|
SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
|
||||||
|
|
||||||
HostTexture.SetData(data);
|
HostTexture.SetData(result);
|
||||||
|
|
||||||
_hasData = true;
|
_hasData = true;
|
||||||
}
|
}
|
||||||
@ -731,7 +727,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Uploads new texture data to the host GPU.
|
/// Uploads new texture data to the host GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">New data</param>
|
/// <param name="data">New data</param>
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(SpanOrArray<byte> data)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
@ -750,7 +746,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="data">New data</param>
|
/// <param name="data">New data</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
@ -786,7 +782,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="level">Mip level to convert</param>
|
/// <param name="level">Mip level to convert</param>
|
||||||
/// <param name="single">True to convert a single slice</param>
|
/// <param name="single">True to convert a single slice</param>
|
||||||
/// <returns>Converted data</returns>
|
/// <returns>Converted data</returns>
|
||||||
public ReadOnlySpan<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||||
{
|
{
|
||||||
int width = Info.Width;
|
int width = Info.Width;
|
||||||
int height = Info.Height;
|
int height = Info.Height;
|
||||||
@ -799,9 +795,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
height = Math.Max(height >> level, 1);
|
height = Math.Max(height >> level, 1);
|
||||||
depth = Math.Max(depth >> level, 1);
|
depth = Math.Max(depth >> level, 1);
|
||||||
|
|
||||||
|
SpanOrArray<byte> result;
|
||||||
|
|
||||||
if (Info.IsLinear)
|
if (Info.IsLinear)
|
||||||
{
|
{
|
||||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
result = LayoutConverter.ConvertLinearStridedToLinear(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
Info.FormatInfo.BlockWidth,
|
Info.FormatInfo.BlockWidth,
|
||||||
@ -813,7 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
data = LayoutConverter.ConvertBlockLinearToLinear(
|
result = LayoutConverter.ConvertBlockLinearToLinear(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
depth,
|
depth,
|
||||||
@ -836,7 +834,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
|
if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
|
||||||
{
|
{
|
||||||
if (!AstcDecoder.TryDecodeToRgba8P(
|
if (!AstcDecoder.TryDecodeToRgba8P(
|
||||||
data.ToArray(),
|
result.ToArray(),
|
||||||
Info.FormatInfo.BlockWidth,
|
Info.FormatInfo.BlockWidth,
|
||||||
Info.FormatInfo.BlockHeight,
|
Info.FormatInfo.BlockHeight,
|
||||||
width,
|
width,
|
||||||
@ -856,11 +854,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers);
|
decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
data = decoded;
|
result = decoded;
|
||||||
}
|
}
|
||||||
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
|
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
|
||||||
{
|
{
|
||||||
data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
|
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result);
|
||||||
}
|
}
|
||||||
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
|
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
|
||||||
{
|
{
|
||||||
@ -868,36 +866,36 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
case Format.Bc1RgbaSrgb:
|
case Format.Bc1RgbaSrgb:
|
||||||
case Format.Bc1RgbaUnorm:
|
case Format.Bc1RgbaUnorm:
|
||||||
data = BCnDecoder.DecodeBC1(data, width, height, depth, levels, layers);
|
result = BCnDecoder.DecodeBC1(result, width, height, depth, levels, layers);
|
||||||
break;
|
break;
|
||||||
case Format.Bc2Srgb:
|
case Format.Bc2Srgb:
|
||||||
case Format.Bc2Unorm:
|
case Format.Bc2Unorm:
|
||||||
data = BCnDecoder.DecodeBC2(data, width, height, depth, levels, layers);
|
result = BCnDecoder.DecodeBC2(result, width, height, depth, levels, layers);
|
||||||
break;
|
break;
|
||||||
case Format.Bc3Srgb:
|
case Format.Bc3Srgb:
|
||||||
case Format.Bc3Unorm:
|
case Format.Bc3Unorm:
|
||||||
data = BCnDecoder.DecodeBC3(data, width, height, depth, levels, layers);
|
result = BCnDecoder.DecodeBC3(result, width, height, depth, levels, layers);
|
||||||
break;
|
break;
|
||||||
case Format.Bc4Snorm:
|
case Format.Bc4Snorm:
|
||||||
case Format.Bc4Unorm:
|
case Format.Bc4Unorm:
|
||||||
data = BCnDecoder.DecodeBC4(data, width, height, depth, levels, layers, Format == Format.Bc4Snorm);
|
result = BCnDecoder.DecodeBC4(result, width, height, depth, levels, layers, Format == Format.Bc4Snorm);
|
||||||
break;
|
break;
|
||||||
case Format.Bc5Snorm:
|
case Format.Bc5Snorm:
|
||||||
case Format.Bc5Unorm:
|
case Format.Bc5Unorm:
|
||||||
data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
|
result = BCnDecoder.DecodeBC5(result, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
|
||||||
break;
|
break;
|
||||||
case Format.Bc6HSfloat:
|
case Format.Bc6HSfloat:
|
||||||
case Format.Bc6HUfloat:
|
case Format.Bc6HUfloat:
|
||||||
data = BCnDecoder.DecodeBC6(data, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
|
result = BCnDecoder.DecodeBC6(result, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
|
||||||
break;
|
break;
|
||||||
case Format.Bc7Srgb:
|
case Format.Bc7Srgb:
|
||||||
case Format.Bc7Unorm:
|
case Format.Bc7Unorm:
|
||||||
data = BCnDecoder.DecodeBC7(data, width, height, depth, levels, layers);
|
result = BCnDecoder.DecodeBC7(result, width, height, depth, levels, layers);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1445,7 +1443,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
DisposeTextures();
|
DisposeTextures();
|
||||||
|
|
||||||
HostTexture = hostTexture;
|
HostTexture = hostTexture;
|
||||||
InvalidatedSequence++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1600,6 +1597,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void DisposeTextures()
|
private void DisposeTextures()
|
||||||
{
|
{
|
||||||
|
InvalidatedSequence++;
|
||||||
|
|
||||||
_currentData = null;
|
_currentData = null;
|
||||||
HostTexture.Release();
|
HostTexture.Release();
|
||||||
|
|
||||||
@ -1634,8 +1633,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
DisposeTextures();
|
DisposeTextures();
|
||||||
|
|
||||||
Disposed?.Invoke(this);
|
|
||||||
|
|
||||||
if (Group.Storage == this)
|
if (Group.Storage == this)
|
||||||
{
|
{
|
||||||
Group.Dispose();
|
Group.Dispose();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Cpu.Tracking;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Cpu.Tracking;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
@ -348,9 +349,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
||||||
|
|
||||||
data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
||||||
|
|
||||||
Storage.SetData(data, info.BaseLayer, info.BaseLevel);
|
Storage.SetData(result, info.BaseLayer, info.BaseLevel);
|
||||||
|
|
||||||
offsetIndex++;
|
offsetIndex++;
|
||||||
}
|
}
|
||||||
|
@ -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 = 3732;
|
private const uint CodeGenVersion = 3759;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -48,17 +49,19 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
return GetData();
|
return GetData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(SpanOrArray<byte> data)
|
||||||
{
|
{
|
||||||
Buffer.SetData(_buffer, _bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize)));
|
var dataSpan = data.AsSpan();
|
||||||
|
|
||||||
|
Buffer.SetData(_buffer, _bufferOffset, dataSpan.Slice(0, Math.Min(dataSpan.Length, _bufferSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -317,32 +318,36 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(SpanOrArray<byte> data)
|
||||||
{
|
{
|
||||||
|
var dataSpan = data.AsSpan();
|
||||||
|
|
||||||
if (Format == Format.S8UintD24Unorm)
|
if (Format == Format.S8UintD24Unorm)
|
||||||
{
|
{
|
||||||
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data)
|
fixed (byte* ptr = dataSpan)
|
||||||
{
|
{
|
||||||
ReadFrom((IntPtr)ptr, data.Length);
|
ReadFrom((IntPtr)ptr, dataSpan.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
|
var dataSpan = data.AsSpan();
|
||||||
|
|
||||||
if (Format == Format.S8UintD24Unorm)
|
if (Format == Format.S8UintD24Unorm)
|
||||||
{
|
{
|
||||||
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data)
|
fixed (byte* ptr = dataSpan)
|
||||||
{
|
{
|
||||||
int width = Math.Max(Info.Width >> level, 1);
|
int width = Math.Max(Info.Width >> level, 1);
|
||||||
int height = Math.Max(Info.Height >> level, 1);
|
int height = Math.Max(Info.Height >> level, 1);
|
||||||
@ -352,11 +357,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
|
var dataSpan = data.AsSpan();
|
||||||
|
|
||||||
if (Format == Format.S8UintD24Unorm)
|
if (Format == Format.S8UintD24Unorm)
|
||||||
{
|
{
|
||||||
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
|
int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
|
||||||
@ -364,7 +371,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data)
|
fixed (byte* ptr = dataSpan)
|
||||||
{
|
{
|
||||||
ReadFrom2D(
|
ReadFrom2D(
|
||||||
(IntPtr)ptr,
|
(IntPtr)ptr,
|
||||||
|
@ -377,6 +377,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
|
|
||||||
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
|
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
|
||||||
{
|
{
|
||||||
|
HashSet<ulong> visited = new HashSet<ulong>();
|
||||||
|
|
||||||
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
|
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
|
||||||
ulong baseOffset = lastOp.GetAbsoluteAddress();
|
ulong baseOffset = lastOp.GetAbsoluteAddress();
|
||||||
|
|
||||||
@ -392,12 +394,17 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
for (int i = 0; i < cbOffsetsCount; i++)
|
for (int i = 0; i < cbOffsetsCount; i++)
|
||||||
{
|
{
|
||||||
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
||||||
Block target = getBlock(baseOffset + targetOffset);
|
ulong targetAddress = baseOffset + targetOffset;
|
||||||
|
|
||||||
|
if (visited.Add(targetAddress))
|
||||||
|
{
|
||||||
|
Block target = getBlock(targetAddress);
|
||||||
target.Predecessors.Add(block);
|
target.Predecessors.Add(block);
|
||||||
block.Successors.Add(target);
|
block.Successors.Add(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return hasNewTarget;
|
return hasNewTarget;
|
||||||
}
|
}
|
||||||
|
@ -41,20 +41,77 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset));
|
Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset));
|
||||||
|
|
||||||
// Sorting the target addresses in descending order improves the code,
|
var targets = context.CurrBlock.Successors.Skip(startIndex);
|
||||||
// since it will always check the most distant targets first, then the
|
|
||||||
// near ones. This can be easily transformed into if/else statements.
|
|
||||||
var sortedTargets = context.CurrBlock.Successors.Skip(startIndex).OrderByDescending(x => x.Address);
|
|
||||||
|
|
||||||
Block lastTarget = sortedTargets.LastOrDefault();
|
bool allTargetsSinglePred = true;
|
||||||
|
int total = context.CurrBlock.Successors.Count - startIndex;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
foreach (Block possibleTarget in sortedTargets)
|
foreach (var target in targets.OrderBy(x => x.Address))
|
||||||
{
|
{
|
||||||
Operand label = context.GetLabel(possibleTarget.Address);
|
if (++count < total && (target.Predecessors.Count > 1 || target.Address <= context.CurrBlock.Address))
|
||||||
|
|
||||||
if (possibleTarget != lastTarget)
|
|
||||||
{
|
{
|
||||||
context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)possibleTarget.Address)));
|
allTargetsSinglePred = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allTargetsSinglePred)
|
||||||
|
{
|
||||||
|
// Chain blocks, each target block will check if the BRX target address
|
||||||
|
// matches its own address, if not, it jumps to the next target which will do the same check,
|
||||||
|
// until it reaches the last possible target, which executed unconditionally.
|
||||||
|
// We can only do this if the BRX block is the only predecessor of all target blocks.
|
||||||
|
// Additionally, this is not supported for blocks located before the current block,
|
||||||
|
// since it will be too late to insert a label, but this is something that can be improved
|
||||||
|
// in the future if necessary.
|
||||||
|
|
||||||
|
var sortedTargets = targets.OrderBy(x => x.Address);
|
||||||
|
|
||||||
|
Block currentTarget = null;
|
||||||
|
ulong firstTargetAddress = 0;
|
||||||
|
|
||||||
|
foreach (Block nextTarget in sortedTargets)
|
||||||
|
{
|
||||||
|
if (currentTarget != null)
|
||||||
|
{
|
||||||
|
if (currentTarget.Address != nextTarget.Address)
|
||||||
|
{
|
||||||
|
context.SetBrxTarget(currentTarget.Address, address, (int)currentTarget.Address, nextTarget.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firstTargetAddress = nextTarget.Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTarget = nextTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Branch(context.GetLabel(firstTargetAddress));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Emit the branches sequentially.
|
||||||
|
// This generates slightly worse code, but should work for all cases.
|
||||||
|
|
||||||
|
var sortedTargets = targets.OrderByDescending(x => x.Address);
|
||||||
|
ulong lastTargetAddress = ulong.MaxValue;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
foreach (Block target in sortedTargets)
|
||||||
|
{
|
||||||
|
Operand label = context.GetLabel(target.Address);
|
||||||
|
|
||||||
|
if (++count < total)
|
||||||
|
{
|
||||||
|
if (target.Address != lastTargetAddress)
|
||||||
|
{
|
||||||
|
context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)target.Address)));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTargetAddress = target.Address;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -62,6 +119,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Cal(EmitterContext context)
|
public static void Cal(EmitterContext context)
|
||||||
{
|
{
|
||||||
|
@ -21,8 +21,33 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public int OperationsCount => _operations.Count;
|
public int OperationsCount => _operations.Count;
|
||||||
|
|
||||||
|
private struct BrxTarget
|
||||||
|
{
|
||||||
|
public readonly Operand Selector;
|
||||||
|
public readonly int ExpectedValue;
|
||||||
|
public readonly ulong NextTargetAddress;
|
||||||
|
|
||||||
|
public BrxTarget(Operand selector, int expectedValue, ulong nextTargetAddress)
|
||||||
|
{
|
||||||
|
Selector = selector;
|
||||||
|
ExpectedValue = expectedValue;
|
||||||
|
NextTargetAddress = nextTargetAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BlockLabel
|
||||||
|
{
|
||||||
|
public readonly Operand Label;
|
||||||
|
public BrxTarget BrxTarget;
|
||||||
|
|
||||||
|
public BlockLabel(Operand label)
|
||||||
|
{
|
||||||
|
Label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly List<Operation> _operations;
|
private readonly List<Operation> _operations;
|
||||||
private readonly Dictionary<ulong, Operand> _labels;
|
private readonly Dictionary<ulong, BlockLabel> _labels;
|
||||||
|
|
||||||
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
||||||
{
|
{
|
||||||
@ -30,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
Config = config;
|
Config = config;
|
||||||
IsNonMain = isNonMain;
|
IsNonMain = isNonMain;
|
||||||
_operations = new List<Operation>();
|
_operations = new List<Operation>();
|
||||||
_labels = new Dictionary<ulong, Operand>();
|
_labels = new Dictionary<ulong, BlockLabel>();
|
||||||
|
|
||||||
EmitStart();
|
EmitStart();
|
||||||
}
|
}
|
||||||
@ -158,14 +183,40 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public Operand GetLabel(ulong address)
|
public Operand GetLabel(ulong address)
|
||||||
{
|
{
|
||||||
if (!_labels.TryGetValue(address, out Operand label))
|
return EnsureBlockLabel(address).Label;
|
||||||
{
|
|
||||||
label = Label();
|
|
||||||
|
|
||||||
_labels.Add(address, label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return label;
|
public void SetBrxTarget(ulong address, Operand selector, int targetValue, ulong nextTargetAddress)
|
||||||
|
{
|
||||||
|
BlockLabel blockLabel = EnsureBlockLabel(address);
|
||||||
|
Debug.Assert(blockLabel.BrxTarget.Selector == null);
|
||||||
|
blockLabel.BrxTarget = new BrxTarget(selector, targetValue, nextTargetAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnterBlock(ulong address)
|
||||||
|
{
|
||||||
|
BlockLabel blockLabel = EnsureBlockLabel(address);
|
||||||
|
|
||||||
|
MarkLabel(blockLabel.Label);
|
||||||
|
|
||||||
|
BrxTarget brxTarget = blockLabel.BrxTarget;
|
||||||
|
|
||||||
|
if (brxTarget.Selector != null)
|
||||||
|
{
|
||||||
|
this.BranchIfFalse(GetLabel(brxTarget.NextTargetAddress), this.ICompareEqual(brxTarget.Selector, Const(brxTarget.ExpectedValue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockLabel EnsureBlockLabel(ulong address)
|
||||||
|
{
|
||||||
|
if (!_labels.TryGetValue(address, out BlockLabel blockLabel))
|
||||||
|
{
|
||||||
|
blockLabel = new BlockLabel(Label());
|
||||||
|
|
||||||
|
_labels.Add(address, blockLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareForVertexReturn()
|
public void PrepareForVertexReturn()
|
||||||
|
@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
context.CurrBlock = block;
|
context.CurrBlock = block;
|
||||||
|
|
||||||
context.MarkLabel(context.GetLabel(block.Address));
|
context.EnterBlock(block.Address);
|
||||||
|
|
||||||
EmitOps(context, block);
|
EmitOps(context, block);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Span<byte> ConvertBlockLinearToLinear(
|
public static byte[] ConvertBlockLinearToLinear(
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int depth,
|
int depth,
|
||||||
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
blockHeight,
|
blockHeight,
|
||||||
bytesPerPixel);
|
bytesPerPixel);
|
||||||
|
|
||||||
Span<byte> output = new byte[outSize];
|
byte[] output = new byte[outSize];
|
||||||
|
|
||||||
int outOffs = 0;
|
int outOffs = 0;
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Span<byte> ConvertLinearStridedToLinear(
|
public static byte[] ConvertLinearStridedToLinear(
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int blockWidth,
|
int blockWidth,
|
||||||
@ -262,14 +262,15 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
|
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
|
||||||
lineSize = Math.Min(lineSize, outStride);
|
lineSize = Math.Min(lineSize, outStride);
|
||||||
|
|
||||||
Span<byte> output = new byte[h * outStride];
|
byte[] output = new byte[h * outStride];
|
||||||
|
Span<byte> outSpan = output;
|
||||||
|
|
||||||
int outOffs = 0;
|
int outOffs = 0;
|
||||||
int inOffs = 0;
|
int inOffs = 0;
|
||||||
|
|
||||||
for (int y = 0; y < h; y++)
|
for (int y = 0; y < h; y++)
|
||||||
{
|
{
|
||||||
data.Slice(inOffs, lineSize).CopyTo(output.Slice(outOffs, lineSize));
|
data.Slice(inOffs, lineSize).CopyTo(outSpan.Slice(outOffs, lineSize));
|
||||||
|
|
||||||
inOffs += stride;
|
inOffs += stride;
|
||||||
outOffs += outStride;
|
outOffs += outStride;
|
||||||
|
@ -386,8 +386,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_waitable.WaitForFences(_gd.Api, _device, offset, size);
|
_waitable.WaitForFences(_gd.Api, _device, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool BoundToRange(int offset, ref int size)
|
||||||
|
{
|
||||||
|
if (offset >= Size)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = Math.Min(Size - offset, size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
|
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
|
||||||
{
|
{
|
||||||
|
if (!BoundToRange(offset, ref size))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var key = new I8ToI16CacheKey(_gd);
|
var key = new I8ToI16CacheKey(_gd);
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||||
@ -407,6 +424,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
|
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
|
||||||
{
|
{
|
||||||
|
if (!BoundToRange(offset, ref size))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
|
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||||
@ -428,6 +450,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
||||||
{
|
{
|
||||||
|
if (!BoundToRange(offset, ref size))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
|
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||||
|
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public int GetPrimitiveCount(int vertexCount)
|
public int GetPrimitiveCount(int vertexCount)
|
||||||
{
|
{
|
||||||
return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
|
return Math.Max(0, (vertexCount - BaseIndex) / IndexStride);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetConvertedCount(int indexCount)
|
public int GetConvertedCount(int indexCount)
|
||||||
|
@ -49,7 +49,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
|
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int bufferSize);
|
||||||
|
|
||||||
|
if (_offset >= bufferSize)
|
||||||
|
{
|
||||||
|
autoBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
offset = _offset;
|
offset = _offset;
|
||||||
size = _size;
|
size = _size;
|
||||||
|
@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
|
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
|
||||||
emptyVb.SetData(0, new byte[EmptyVbSize]);
|
emptyVb.SetData(0, new byte[EmptyVbSize]);
|
||||||
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0);
|
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize, 0);
|
||||||
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
|
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
|
||||||
|
|
||||||
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
|
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
|
||||||
@ -1243,7 +1243,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
|
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
|
||||||
|
|
||||||
_vertexBuffersDirty &= ~(1u << i);
|
_vertexBuffersDirty &= ~(1UL << i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
};
|
};
|
||||||
|
|
||||||
samplerCreateInfo.PNext = &customBorderColor;
|
samplerCreateInfo.PNext = &customBorderColor;
|
||||||
|
samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
|
gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -90,17 +91,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_bufferView = null;
|
_bufferView = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(SpanOrArray<byte> data)
|
||||||
{
|
{
|
||||||
_gd.SetBufferData(_bufferHandle, _offset, data);
|
_gd.SetBufferData(_bufferHandle, _offset, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
@ -480,6 +480,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (--_viewsCount == 0)
|
if (--_viewsCount == 0)
|
||||||
{
|
{
|
||||||
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
|
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
|
||||||
|
|
||||||
|
Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -514,10 +515,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dst.Info,
|
dst.Info,
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
src.FirstLevel,
|
|
||||||
dst.FirstLevel,
|
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
|
src.FirstLevel,
|
||||||
|
dst.FirstLevel,
|
||||||
layers,
|
layers,
|
||||||
levels,
|
levels,
|
||||||
linearFilter,
|
linearFilter,
|
||||||
@ -873,17 +874,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return GetDataFromBuffer(result, size, result);
|
return GetDataFromBuffer(result, size, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(SpanOrArray<byte> data)
|
||||||
{
|
{
|
||||||
SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
|
SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
SetData(data, layer, level, 1, 1, singleSlice: true);
|
SetData(data, layer, level, 1, 1, singleSlice: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
SetData(data, layer, level, 1, 1, singleSlice: true, region);
|
SetData(data, layer, level, 1, 1, singleSlice: true, region);
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
|
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
|
||||||
{
|
{
|
||||||
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
|
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
|
||||||
int stride = (_stride + (alignment - 1)) & -alignment;
|
|
||||||
|
|
||||||
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
|
if (autoBuffer != null)
|
||||||
|
{
|
||||||
|
int stride = (_stride + (alignment - 1)) & -alignment;
|
||||||
|
int newSize = (_size / _stride) * stride;
|
||||||
|
|
||||||
|
var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsExtendedDynamicState)
|
if (gd.Capabilities.SupportsExtendedDynamicState)
|
||||||
{
|
{
|
||||||
@ -69,7 +73,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
1,
|
1,
|
||||||
buffer,
|
buffer,
|
||||||
0,
|
0,
|
||||||
(ulong)(_size / _stride) * (ulong)stride,
|
(ulong)newSize,
|
||||||
(ulong)stride);
|
(ulong)stride);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -78,16 +82,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buffer = autoBuffer;
|
_buffer = autoBuffer;
|
||||||
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
|
}
|
||||||
|
|
||||||
|
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
|
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
|
||||||
|
|
||||||
// The original stride must be reapplied in case it was rewritten.
|
// The original stride must be reapplied in case it was rewritten.
|
||||||
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
||||||
|
|
||||||
|
if (_offset >= size)
|
||||||
|
{
|
||||||
|
autoBuffer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal TextureView CreateTextureView(TextureCreateInfo info, float scale)
|
internal TextureView CreateTextureView(TextureCreateInfo info, float scale)
|
||||||
{
|
{
|
||||||
// This should be disposed when all views are destroyed.
|
// This should be disposed when all views are destroyed.
|
||||||
using var storage = CreateTextureStorage(info, scale);
|
var storage = CreateTextureStorage(info, scale);
|
||||||
return storage.CreateView(info, 0, 0);
|
return storage.CreateView(info, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return int.Parse(part.Substring(0, numberLength));
|
return int.Parse(part.AsSpan(0, numberLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseNumber(bool isSigned = false)
|
private string ParseNumber(bool isSigned = false)
|
||||||
|
@ -2540,11 +2540,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
for (int attempt = 0; attempt < 8; attempt++)
|
for (int attempt = 0; attempt < 8; attempt++)
|
||||||
{
|
{
|
||||||
address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
|
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
|
||||||
|
ulong aslrEndAddr = aslrAddress + totalNeededSize;
|
||||||
|
|
||||||
ulong endAddr = address + totalNeededSize;
|
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
|
||||||
|
|
||||||
KMemoryInfo info = _blockManager.FindBlock(address).GetInfo();
|
|
||||||
|
|
||||||
if (info.State != MemoryState.Unmapped)
|
if (info.State != MemoryState.Unmapped)
|
||||||
{
|
{
|
||||||
@ -2554,11 +2553,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
||||||
ulong currEndAddr = info.Address + info.Size;
|
ulong currEndAddr = info.Address + info.Size;
|
||||||
|
|
||||||
if (address >= regionStart &&
|
if (aslrAddress >= regionStart &&
|
||||||
address >= currBaseAddr &&
|
aslrAddress >= currBaseAddr &&
|
||||||
endAddr - 1 <= regionEndAddr - 1 &&
|
aslrEndAddr - 1 <= regionEndAddr - 1 &&
|
||||||
endAddr - 1 <= currEndAddr - 1)
|
aslrEndAddr - 1 <= currEndAddr - 1)
|
||||||
{
|
{
|
||||||
|
address = aslrAddress;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2603,7 +2603,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
|
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
|
||||||
|
|
||||||
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
|
ulong regionEndAddr = (regionStart + regionPagesCount * PageSize) - 1;
|
||||||
|
|
||||||
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);
|
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||||
@ -787,6 +788,26 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(205)]
|
||||||
|
// OpenDataStorageWithProgramIndex(u8 program_index) -> object<nn::fssrv::sf::IStorage>
|
||||||
|
public ResultCode OpenDataStorageWithProgramIndex(ServiceCtx context)
|
||||||
|
{
|
||||||
|
byte programIndex = context.RequestData.ReadByte();
|
||||||
|
|
||||||
|
if ((context.Device.Application.TitleId & 0xf) != programIndex)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
var storage = context.Device.FileSystem.RomFs.AsStorage(true);
|
||||||
|
using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage);
|
||||||
|
using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref()));
|
||||||
|
|
||||||
|
MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref()));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(400)]
|
[CommandHipc(400)]
|
||||||
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
||||||
public ResultCode OpenDeviceOperator(ServiceCtx context)
|
public ResultCode OpenDeviceOperator(ServiceCtx context)
|
||||||
|
@ -5,22 +5,48 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
|
|||||||
class INfc : IpcService
|
class INfc : IpcService
|
||||||
{
|
{
|
||||||
private NfcPermissionLevel _permissionLevel;
|
private NfcPermissionLevel _permissionLevel;
|
||||||
|
private State _state;
|
||||||
|
|
||||||
public INfc(NfcPermissionLevel permissionLevel)
|
public INfc(NfcPermissionLevel permissionLevel)
|
||||||
{
|
{
|
||||||
_permissionLevel = permissionLevel;
|
_permissionLevel = permissionLevel;
|
||||||
|
_state = State.NonInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(0)]
|
[CommandHipc(0)]
|
||||||
[CommandHipc(400)] // 4.0.0+
|
[CommandHipc(400)] // 4.0.0+
|
||||||
// Initialize()
|
// Initialize(u64, u64, pid, buffer<unknown, 5>)
|
||||||
public ResultCode Initialize(ServiceCtx context)
|
public ResultCode Initialize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
_state = State.Initialized;
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
|
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(1)]
|
||||||
|
[CommandHipc(401)] // 4.0.0+
|
||||||
|
// Finalize()
|
||||||
|
public ResultCode Finalize(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_state = State.NonInitialized;
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(2)]
|
||||||
|
[CommandHipc(402)] // 4.0.0+
|
||||||
|
// GetState() -> u32
|
||||||
|
public ResultCode GetState(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((int)_state);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(3)]
|
[CommandHipc(3)]
|
||||||
[CommandHipc(403)] // 4.0.0+
|
[CommandHipc(403)] // 4.0.0+
|
||||||
// IsNfcEnabled() -> b8
|
// IsNfcEnabled() -> b8
|
||||||
|
8
Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs
Normal file
8
Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
NonInitialized,
|
||||||
|
Initialized
|
||||||
|
}
|
||||||
|
}
|
@ -816,11 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||||||
Reserved = new Array57<byte>()
|
Reserved = new Array57<byte>()
|
||||||
};
|
};
|
||||||
|
|
||||||
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
|
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber));
|
||||||
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
|
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber);
|
||||||
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
|
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber);
|
||||||
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
|
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber);
|
||||||
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
|
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber);
|
||||||
|
|
||||||
context.Memory.Write(outputPosition, modelInfo);
|
context.Memory.Write(outputPosition, modelInfo);
|
||||||
|
|
||||||
|
@ -235,6 +235,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
shouldBlockAfterOperation = true;
|
shouldBlockAfterOperation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Socket.IsBound)
|
||||||
|
{
|
||||||
|
receiveSize = -1;
|
||||||
|
|
||||||
|
return LinuxError.EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
receiveSize = Socket.ReceiveFrom(buffer[..size], ConvertBsdSocketFlags(flags), ref temp);
|
receiveSize = Socket.ReceiveFrom(buffer[..size], ConvertBsdSocketFlags(flags), ref temp);
|
||||||
|
|
||||||
remoteEndPoint = (IPEndPoint)temp;
|
remoteEndPoint = (IPEndPoint)temp;
|
||||||
|
@ -349,7 +349,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
|
|||||||
// GetSessionCacheMode() -> nn::ssl::sf::SessionCacheMode
|
// GetSessionCacheMode() -> nn::ssl::sf::SessionCacheMode
|
||||||
public ResultCode GetSessionCacheMode(ServiceCtx context)
|
public ResultCode GetSessionCacheMode(ServiceCtx context)
|
||||||
{
|
{
|
||||||
throw new ServiceNotImplementedException(this, context);
|
context.ResponseData.Write((uint)_sessionCacheMode);
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { _sessionCacheMode });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(19)]
|
[CommandHipc(19)]
|
||||||
|
@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
|
|||||||
|
|
||||||
public bool Evaluate()
|
public bool Evaluate()
|
||||||
{
|
{
|
||||||
return (_input.Value & _mask) != 0;
|
return (_input.Value & _mask) == _mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,9 +122,8 @@ namespace Ryujinx.HLE.HOS.Tamper
|
|||||||
for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++)
|
for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++)
|
||||||
{
|
{
|
||||||
int index = wordIndex * wordSize + nybbleIndex;
|
int index = wordIndex * wordSize + nybbleIndex;
|
||||||
string byteData = word.Substring(nybbleIndex, 1);
|
|
||||||
|
|
||||||
instruction[index] = byte.Parse(byteData, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
instruction[index] = byte.Parse(word.AsSpan(nybbleIndex, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ namespace Ryujinx.HLE.Loaders.Mods
|
|||||||
{
|
{
|
||||||
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|
||||||
{
|
{
|
||||||
return int.TryParse(str.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out value);
|
return int.TryParse(str.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
|
|
||||||
for (int index = 0; index < bytesInHex; index++)
|
for (int index = 0; index < bytesInHex; index++)
|
||||||
{
|
{
|
||||||
output[index] = byte.Parse(hexString.Substring(index * 2, 2), NumberStyles.HexNumber);
|
output[index] = byte.Parse(hexString.AsSpan(index * 2, 2), NumberStyles.HexNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -51,7 +51,16 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_device.Hid.RefreshInputConfig(_inputConfig);
|
List<InputConfig> validInputs = new List<InputConfig>();
|
||||||
|
foreach (var inputConfigEntry in _inputConfig)
|
||||||
|
{
|
||||||
|
if (_controllers[(int)inputConfigEntry.PlayerIndex] != null)
|
||||||
|
{
|
||||||
|
validInputs.Add(inputConfigEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_device.Hid.RefreshInputConfig(validInputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +112,8 @@ namespace Ryujinx.Input.HLE
|
|||||||
_controllers[i] = null;
|
_controllers[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<InputConfig> validInputs = new List<InputConfig>();
|
||||||
|
|
||||||
foreach (InputConfig inputConfigEntry in inputConfig)
|
foreach (InputConfig inputConfigEntry in inputConfig)
|
||||||
{
|
{
|
||||||
NpadController controller = new NpadController(_cemuHookClient);
|
NpadController controller = new NpadController(_cemuHookClient);
|
||||||
@ -116,6 +127,7 @@ namespace Ryujinx.Input.HLE
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
|
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
|
||||||
|
validInputs.Add(inputConfigEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +135,7 @@ namespace Ryujinx.Input.HLE
|
|||||||
_enableKeyboard = enableKeyboard;
|
_enableKeyboard = enableKeyboard;
|
||||||
_enableMouse = enableMouse;
|
_enableMouse = enableMouse;
|
||||||
|
|
||||||
_device.Hid.RefreshInputConfig(inputConfig);
|
_device.Hid.RefreshInputConfig(validInputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,5 +92,31 @@ namespace Ryujinx.Memory.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_AliasMapLeak()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
// Memory aliasing tests fail on CI at the moment.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pageSize = 4096;
|
||||||
|
ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that.
|
||||||
|
|
||||||
|
using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable);
|
||||||
|
using MemoryBlock toAlias = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
|
||||||
|
|
||||||
|
for (ulong offset = 0; offset < size; offset += pageSize)
|
||||||
|
{
|
||||||
|
toAlias.MapView(backing, 0, offset, pageSize);
|
||||||
|
|
||||||
|
toAlias.Write(offset, 0xbadc0de);
|
||||||
|
Assert.AreEqual(0xbadc0de, backing.Read<int>(0));
|
||||||
|
|
||||||
|
toAlias.UnmapView(backing, offset, pageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -177,7 +177,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
public static void UnmapView(IntPtr location, ulong size)
|
public static void UnmapView(IntPtr location, ulong size)
|
||||||
{
|
{
|
||||||
mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED, -1, 0);
|
mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -339,6 +339,93 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
CompareAgainstUnicorn();
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Explicit]
|
||||||
|
[Test, Pairwise, Description("VCVT<top>.F16.F32 <Sd>, <Dm>")]
|
||||||
|
public void Vcvt_F32_F16([Values(0u, 1u, 2u, 3u)] uint rd,
|
||||||
|
[Values(0u, 1u, 2u, 3u)] uint rm,
|
||||||
|
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0,
|
||||||
|
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1,
|
||||||
|
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2,
|
||||||
|
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3,
|
||||||
|
[Values] bool top)
|
||||||
|
{
|
||||||
|
uint opcode = 0xeeb30a40; // VCVTB.F16.F32 S0, D0
|
||||||
|
|
||||||
|
if (top)
|
||||||
|
{
|
||||||
|
opcode |= 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
|
||||||
|
opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5);
|
||||||
|
|
||||||
|
V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3);
|
||||||
|
|
||||||
|
SingleOpcode(opcode, v0: v0);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Explicit]
|
||||||
|
[Test, Pairwise, Description("VCVT<top>.F16.F64 <Sd>, <Dm>")]
|
||||||
|
public void Vcvt_F64_F16([Values(0u, 1u, 2u, 3u)] uint rd,
|
||||||
|
[Values(0u, 1u)] uint rm,
|
||||||
|
[ValueSource(nameof(_1D_F_))] ulong d0,
|
||||||
|
[ValueSource(nameof(_1D_F_))] ulong d1,
|
||||||
|
[Values] bool top)
|
||||||
|
{
|
||||||
|
uint opcode = 0xeeb30b40; // VCVTB.F16.F64 S0, D0
|
||||||
|
|
||||||
|
if (top)
|
||||||
|
{
|
||||||
|
opcode |= 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
|
||||||
|
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||||
|
|
||||||
|
V128 v0 = MakeVectorE0E1(d0, d1);
|
||||||
|
|
||||||
|
SingleOpcode(opcode, v0: v0);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Explicit]
|
||||||
|
[Test, Pairwise, Description("VCVT<top>.F<size>.F16 <Vd>, <Sm>")]
|
||||||
|
public void Vcvt_F16_Fx([Values(0u, 1u, 2u, 3u)] uint rd,
|
||||||
|
[Values(0u, 1u, 2u, 3u)] uint rm,
|
||||||
|
[ValueSource(nameof(_1D_F_))] ulong d0,
|
||||||
|
[ValueSource(nameof(_1D_F_))] ulong d1,
|
||||||
|
[Values] bool top,
|
||||||
|
[Values] bool sz)
|
||||||
|
{
|
||||||
|
uint opcode = 0xeeb20a40; // VCVTB.F32.F16 S0, S0
|
||||||
|
|
||||||
|
if (top)
|
||||||
|
{
|
||||||
|
opcode |= 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sz)
|
||||||
|
{
|
||||||
|
opcode |= 1 << 8;
|
||||||
|
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||||
|
|
||||||
|
V128 v0 = MakeVectorE0E1(d0, d1);
|
||||||
|
|
||||||
|
SingleOpcode(opcode, v0: v0);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
||||||
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||||
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
|
var ryuArg = Environment.GetCommandLineArgs().AsEnumerable().Skip(1);
|
||||||
|
|
||||||
Process.Start(ryuExe, ryuArg);
|
Process.Start(ryuExe, ryuArg);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user