Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9c2500de5f | ||
|
dbe43c1719 | ||
|
f502cfaf62 | ||
|
1fd5cf2b4a | ||
|
814f75142e | ||
|
4c0eb91d7e | ||
|
da75a9a6ea | ||
|
41790aa743 | ||
|
0cb1e926b5 |
@@ -726,7 +726,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
|
||||
{
|
||||
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)), op1, op2);
|
||||
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum), op1, op2);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -774,7 +774,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
|
||||
{
|
||||
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)), op1, op2);
|
||||
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -900,7 +900,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
|
||||
{
|
||||
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)), op1, op2);
|
||||
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum), op1, op2);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -948,7 +948,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorAcrossVectorOpF(context, (op1, op2) =>
|
||||
{
|
||||
return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)), op1, op2);
|
||||
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1633,37 +1633,17 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Frinti_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
if (op.Size == 0)
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
|
||||
}
|
||||
else /* if (op.Size == 1) */
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
|
||||
}
|
||||
return EmitRoundByRMode(context, op1);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Frinti_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
if (sizeF == 0)
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
|
||||
}
|
||||
return EmitRoundByRMode(context, op1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1759,37 +1739,17 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Frintx_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
if (op.Size == 0)
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
|
||||
}
|
||||
else /* if (op.Size == 1) */
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
|
||||
}
|
||||
return EmitRoundByRMode(context, op1);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Frintx_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
if (sizeF == 0)
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1);
|
||||
}
|
||||
return EmitRoundByRMode(context, op1);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
@@ -178,37 +177,20 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero)
|
||||
{
|
||||
Operand one = Const(1);
|
||||
if (zero)
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) =>
|
||||
{
|
||||
OperandType type = m.Type;
|
||||
Operand zeroOp = m.Type == OperandType.FP64 ? ConstF(0.0d) : ConstF(0.0f);
|
||||
|
||||
if (type == OperandType.FP64)
|
||||
{
|
||||
return context.Call(typeof(SoftFloat64).GetMethod(name), m, ConstF(0.0d), one);
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.Call(typeof(SoftFloat32).GetMethod(name), m, ConstF(0.0f), one);
|
||||
}
|
||||
return EmitSoftFloatCallDefaultFpscr(context, name, m, zeroOp);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpF32(context, (n, m) =>
|
||||
{
|
||||
OperandType type = n.Type;
|
||||
|
||||
if (type == OperandType.FP64)
|
||||
{
|
||||
return context.Call(typeof(SoftFloat64).GetMethod(name), n, m, one);
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.Call(typeof(SoftFloat32).GetMethod(name), n, m, one);
|
||||
}
|
||||
return EmitSoftFloatCallDefaultFpscr(context, name, n, m);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -357,11 +339,7 @@ namespace ARMeilleure.Instructions
|
||||
me = ExtractScalar(context, type, op.Vm);
|
||||
}
|
||||
|
||||
MethodInfo info = sizeF != 0
|
||||
? typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare))
|
||||
: typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare));
|
||||
|
||||
Operand nzcv = context.Call(info, ne, me, Const(signalNaNs));
|
||||
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
|
||||
|
||||
EmitSetFpscrNzcv(context, nzcv);
|
||||
}
|
||||
|
@@ -76,7 +76,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0);
|
||||
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
res = context.ZeroExtend16(OperandType.I64, res);
|
||||
|
||||
@@ -98,7 +100,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1);
|
||||
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
|
||||
}
|
||||
@@ -120,7 +124,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand ne = context.VectorExtract(OperandType.FP64, GetVec(op.Rn), 0);
|
||||
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
res = context.ZeroExtend16(OperandType.I64, res);
|
||||
|
||||
@@ -143,7 +149,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1);
|
||||
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
|
||||
}
|
||||
@@ -224,7 +232,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, part + index, 1);
|
||||
|
||||
context.StoreToContext();
|
||||
Operand e = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
res = context.VectorInsert(res, e, index);
|
||||
}
|
||||
@@ -333,7 +343,9 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
if (sizeF == 0)
|
||||
{
|
||||
context.StoreToContext();
|
||||
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
e = context.ZeroExtend16(OperandType.I64, e);
|
||||
|
||||
|
@@ -161,33 +161,14 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
||||
|
||||
Operand asInteger;
|
||||
|
||||
// TODO: Fast Path.
|
||||
if (roundWithFpscr)
|
||||
{
|
||||
MethodInfo info;
|
||||
|
||||
if (floatSize == OperandType.FP64)
|
||||
{
|
||||
info = unsigned
|
||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))
|
||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32));
|
||||
}
|
||||
else
|
||||
{
|
||||
info = unsigned
|
||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))
|
||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32));
|
||||
toConvert = EmitRoundByRMode(context, toConvert);
|
||||
}
|
||||
|
||||
asInteger = context.Call(info, toConvert);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Round towards zero.
|
||||
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
||||
}
|
||||
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
||||
|
||||
InsertScalar(context, op.Vd, asInteger);
|
||||
}
|
||||
@@ -271,9 +252,7 @@ namespace ARMeilleure.Instructions
|
||||
break;
|
||||
}
|
||||
|
||||
Operand asInteger;
|
||||
|
||||
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
||||
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
||||
|
||||
InsertScalar(context, op.Vd, asInteger);
|
||||
}
|
||||
@@ -399,15 +378,9 @@ namespace ARMeilleure.Instructions
|
||||
// VRINTX (floating-point).
|
||||
public static void Vrintx_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
bool doubleSize = (op.Size & 1) == 1;
|
||||
string methodName = doubleSize ? nameof(SoftFallback.Round) : nameof(SoftFallback.RoundF);
|
||||
|
||||
EmitScalarUnaryOpF32(context, (op1) =>
|
||||
{
|
||||
MethodInfo info = typeof(SoftFallback).GetMethod(methodName);
|
||||
return context.Call(info, op1);
|
||||
return EmitRoundByRMode(context, op1);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -361,6 +361,54 @@ namespace ARMeilleure.Instructions
|
||||
return context.Call(info, n, Const((int)roundMode));
|
||||
}
|
||||
|
||||
public static Operand EmitGetRoundingMode(ArmEmitterContext context)
|
||||
{
|
||||
Operand rMode = context.ShiftLeft(GetFpFlag(FPState.RMode1Flag), Const(1));
|
||||
rMode = context.BitwiseOr(rMode, GetFpFlag(FPState.RMode0Flag));
|
||||
|
||||
return rMode;
|
||||
}
|
||||
|
||||
public static Operand EmitRoundByRMode(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.FP32 || op.Type == OperandType.FP64);
|
||||
|
||||
Operand lbl1 = Label();
|
||||
Operand lbl2 = Label();
|
||||
Operand lbl3 = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand rN = Const((int)FPRoundingMode.ToNearest);
|
||||
Operand rP = Const((int)FPRoundingMode.TowardsPlusInfinity);
|
||||
Operand rM = Const((int)FPRoundingMode.TowardsMinusInfinity);
|
||||
|
||||
Operand res = context.AllocateLocal(op.Type);
|
||||
|
||||
Operand rMode = EmitGetRoundingMode(context);
|
||||
|
||||
context.BranchIf(lbl1, rMode, rN, Comparison.NotEqual);
|
||||
context.Copy(res, EmitRoundMathCall(context, MidpointRounding.ToEven, op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl1);
|
||||
context.BranchIf(lbl2, rMode, rP, Comparison.NotEqual);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Ceiling), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl2);
|
||||
context.BranchIf(lbl3, rMode, rM, Comparison.NotEqual);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Floor), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl3);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Truncate), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static Operand EmitSoftFloatCall(ArmEmitterContext context, string name, params Operand[] callArgs)
|
||||
{
|
||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||
@@ -369,7 +417,11 @@ namespace ARMeilleure.Instructions
|
||||
? typeof(SoftFloat32).GetMethod(name)
|
||||
: typeof(SoftFloat64).GetMethod(name);
|
||||
|
||||
return context.Call(info, callArgs);
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(info, callArgs);
|
||||
context.LoadFromContext();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpByElemF(ArmEmitterContext context, Func2I emit)
|
||||
@@ -1269,7 +1321,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void EmitSseOrAvxEnterFtzAndDazModesOpF(ArmEmitterContext context, out Operand isTrue)
|
||||
{
|
||||
isTrue = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)));
|
||||
isTrue = GetFpFlag(FPState.FzFlag);
|
||||
|
||||
Operand lblTrue = Label();
|
||||
context.BranchIfFalse(lblTrue, isTrue);
|
||||
@@ -1281,9 +1333,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default)
|
||||
{
|
||||
isTrue = isTrue == default
|
||||
? context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)))
|
||||
: isTrue;
|
||||
isTrue = isTrue == default ? GetFpFlag(FPState.FzFlag) : isTrue;
|
||||
|
||||
Operand lblTrue = Label();
|
||||
context.BranchIfFalse(lblTrue, isTrue);
|
||||
@@ -1552,13 +1602,13 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lbl1, op, zeroL, Comparison.LessOrEqual);
|
||||
context.Copy(res, maxT);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl1);
|
||||
context.BranchIf(lblEnd, op, zeroL, Comparison.GreaterOrEqual);
|
||||
context.Copy(res, minT);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1583,7 +1633,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lblEnd, op, zeroUL, Comparison.LessOrEqualUI);
|
||||
context.Copy(res, maxT);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1610,13 +1660,13 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lbl1, op, maxT, Comparison.LessOrEqual);
|
||||
context.Copy(res, maxT);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl1);
|
||||
context.BranchIf(lblEnd, op, minT, Comparison.GreaterOrEqual);
|
||||
context.Copy(res, minT);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1641,7 +1691,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lblEnd, op, maxT, Comparison.LessOrEqualUI);
|
||||
context.Copy(res, maxT);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1663,7 +1713,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lblEnd, op, minL, Comparison.NotEqual);
|
||||
context.Copy(res, maxL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1691,7 +1741,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Operand isPositive = context.ICompareGreaterOrEqual(op1, zeroL);
|
||||
context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL));
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1713,7 +1763,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lblEnd, add, op1, Comparison.GreaterOrEqualUI);
|
||||
context.Copy(res, maxUL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1741,7 +1791,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Operand isPositive = context.ICompareGreaterOrEqual(op1, zeroL);
|
||||
context.Copy(res, context.ConditionalSelect(isPositive, maxL, minL));
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1763,7 +1813,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIf(lblEnd, op1, op2, Comparison.GreaterOrEqualUI);
|
||||
context.Copy(res, zeroL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1790,19 +1840,19 @@ namespace ARMeilleure.Instructions
|
||||
Operand notOp2AndRes = context.BitwiseAnd(context.BitwiseNot(op2), add);
|
||||
context.BranchIf(lblEnd, notOp2AndRes, zeroL, Comparison.GreaterOrEqual);
|
||||
context.Copy(res, maxL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl1);
|
||||
context.BranchIf(lbl2, op2, zeroL, Comparison.Less);
|
||||
context.Copy(res, maxL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl2);
|
||||
context.BranchIf(lblEnd, add, maxL, Comparison.LessOrEqualUI);
|
||||
context.Copy(res, maxL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@@ -1828,14 +1878,14 @@ namespace ARMeilleure.Instructions
|
||||
context.BranchIf(lbl1, op1, zeroL, Comparison.Less);
|
||||
context.BranchIf(lblEnd, add, op1, Comparison.GreaterOrEqualUI);
|
||||
context.Copy(res, maxUL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl1);
|
||||
context.BranchIf(lblEnd, op2, maxL, Comparison.GreaterUI);
|
||||
context.BranchIf(lblEnd, add, zeroL, Comparison.GreaterOrEqual);
|
||||
context.Copy(res, zeroL);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
@@ -1181,7 +1181,11 @@ namespace ARMeilleure.Instructions
|
||||
Array.Resize(ref callArgs, callArgs.Length + 1);
|
||||
callArgs[callArgs.Length - 1] = Const(1);
|
||||
|
||||
return context.Call(info, callArgs);
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(info, callArgs);
|
||||
context.LoadFromContext();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@@ -378,7 +379,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIfFalse(lblNoSat, context.BitwiseOr(gt, lt));
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
|
@@ -31,8 +31,8 @@ namespace ARMeilleure.Instructions
|
||||
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
|
||||
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
|
||||
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
||||
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
|
||||
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
|
||||
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
|
||||
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
|
||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
||||
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
|
||||
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
||||
@@ -54,8 +54,8 @@ namespace ARMeilleure.Instructions
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
||||
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)); break;
|
||||
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)); break;
|
||||
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
|
||||
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
|
||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
@@ -121,39 +121,91 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag));
|
||||
Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag));
|
||||
Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag));
|
||||
Operand nzcv = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
|
||||
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
|
||||
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
|
||||
|
||||
Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
|
||||
SetIntOrZR(context, op.Rt, nzcv);
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, nzcvSh);
|
||||
private static void EmitGetFpcr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand fpcr = Const(0);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
|
||||
{
|
||||
fpcr = context.BitwiseOr(fpcr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
|
||||
}
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, fpcr);
|
||||
}
|
||||
|
||||
private static void EmitGetFpsr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand fpsr = Const(0);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
|
||||
{
|
||||
fpsr = context.BitwiseOr(fpsr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
|
||||
}
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, fpsr);
|
||||
}
|
||||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand t = GetIntOrZR(context, op.Rt);
|
||||
t = context.ConvertI64ToI32(t);
|
||||
Operand nzcv = GetIntOrZR(context, op.Rt);
|
||||
nzcv = context.ConvertI64ToI32(nzcv);
|
||||
|
||||
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
|
||||
v = context.BitwiseAnd (v, Const(1));
|
||||
SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.VFlag)), Const(1)));
|
||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.CFlag)), Const(1)));
|
||||
SetFlag(context, PState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.ZFlag)), Const(1)));
|
||||
SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.NFlag)), Const(1)));
|
||||
}
|
||||
|
||||
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
|
||||
c = context.BitwiseAnd (c, Const(1));
|
||||
private static void EmitSetFpcr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
|
||||
z = context.BitwiseAnd (z, Const(1));
|
||||
Operand fpcr = GetIntOrZR(context, op.Rt);
|
||||
fpcr = context.ConvertI64ToI32(fpcr);
|
||||
|
||||
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
|
||||
n = context.BitwiseAnd (n, Const(1));
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
|
||||
{
|
||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetFlag(context, PState.VFlag, v);
|
||||
SetFlag(context, PState.CFlag, c);
|
||||
SetFlag(context, PState.ZFlag, z);
|
||||
SetFlag(context, PState.NFlag, n);
|
||||
private static void EmitSetFpsr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand fpsr = GetIntOrZR(context, op.Rt);
|
||||
fpsr = context.ConvertI64ToI32(fpsr);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
|
||||
{
|
||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -169,14 +169,11 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag));
|
||||
Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag));
|
||||
Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag));
|
||||
Operand qSh = context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag));
|
||||
|
||||
Operand spsr = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
|
||||
spsr = context.BitwiseOr(spsr, qSh);
|
||||
Operand spsr = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag)));
|
||||
|
||||
// TODO: Remaining flags.
|
||||
|
||||
@@ -200,8 +197,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
EmitSetNzcv(context, value);
|
||||
|
||||
Operand q = context.ShiftRightUI(value, Const((int)PState.QFlag));
|
||||
q = context.BitwiseAnd(q, Const(1));
|
||||
Operand q = context.BitwiseAnd(context.ShiftRightUI(value, Const((int)PState.QFlag)), Const(1));
|
||||
|
||||
SetFlag(context, PState.QFlag, q);
|
||||
}
|
||||
@@ -284,17 +280,10 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
|
||||
{
|
||||
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
|
||||
v = context.BitwiseAnd(v, Const(1));
|
||||
|
||||
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
|
||||
c = context.BitwiseAnd(c, Const(1));
|
||||
|
||||
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
|
||||
z = context.BitwiseAnd(z, Const(1));
|
||||
|
||||
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
|
||||
n = context.BitwiseAnd(n, Const(1));
|
||||
Operand v = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.VFlag)), Const(1));
|
||||
Operand c = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.CFlag)), Const(1));
|
||||
Operand z = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.ZFlag)), Const(1));
|
||||
Operand n = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.NFlag)), Const(1));
|
||||
|
||||
SetFlag(context, PState.VFlag, v);
|
||||
SetFlag(context, PState.CFlag, c);
|
||||
@@ -306,42 +295,32 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
||||
|
||||
Operand vSh = context.ShiftLeft(GetFpFlag(FPState.VFlag), Const((int)FPState.VFlag));
|
||||
Operand cSh = context.ShiftLeft(GetFpFlag(FPState.CFlag), Const((int)FPState.CFlag));
|
||||
Operand zSh = context.ShiftLeft(GetFpFlag(FPState.ZFlag), Const((int)FPState.ZFlag));
|
||||
Operand nSh = context.ShiftLeft(GetFpFlag(FPState.NFlag), Const((int)FPState.NFlag));
|
||||
Operand fpscr = Const(0);
|
||||
|
||||
Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
|
||||
{
|
||||
fpscr = context.BitwiseOr(fpscr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
|
||||
}
|
||||
}
|
||||
|
||||
Operand fpscr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr)));
|
||||
|
||||
SetIntA32(context, op.Rt, context.BitwiseOr(nzcvSh, fpscr));
|
||||
SetIntA32(context, op.Rt, fpscr);
|
||||
}
|
||||
|
||||
private static void EmitSetFpscr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
||||
|
||||
Operand t = GetIntA32(context, op.Rt);
|
||||
Operand fpscr = GetIntA32(context, op.Rt);
|
||||
|
||||
Operand v = context.ShiftRightUI(t, Const((int)FPState.VFlag));
|
||||
v = context.BitwiseAnd(v, Const(1));
|
||||
|
||||
Operand c = context.ShiftRightUI(t, Const((int)FPState.CFlag));
|
||||
c = context.BitwiseAnd(c, Const(1));
|
||||
|
||||
Operand z = context.ShiftRightUI(t, Const((int)FPState.ZFlag));
|
||||
z = context.BitwiseAnd(z, Const(1));
|
||||
|
||||
Operand n = context.ShiftRightUI(t, Const((int)FPState.NFlag));
|
||||
n = context.BitwiseAnd(n, Const(1));
|
||||
|
||||
SetFpFlag(context, FPState.VFlag, v);
|
||||
SetFpFlag(context, FPState.CFlag, c);
|
||||
SetFpFlag(context, FPState.ZFlag, z);
|
||||
SetFpFlag(context, FPState.NFlag, n);
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr)), t);
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
|
||||
{
|
||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -72,29 +72,6 @@ namespace ARMeilleure.Instructions
|
||||
return (ulong)GetContext().DczidEl0;
|
||||
}
|
||||
|
||||
public static ulong GetFpcr()
|
||||
{
|
||||
return (ulong)GetContext().Fpcr;
|
||||
}
|
||||
|
||||
public static bool GetFpcrFz()
|
||||
{
|
||||
return (GetContext().Fpcr & FPCR.Fz) != 0;
|
||||
}
|
||||
|
||||
public static ulong GetFpsr()
|
||||
{
|
||||
return (ulong)GetContext().Fpsr;
|
||||
}
|
||||
|
||||
public static uint GetFpscr()
|
||||
{
|
||||
ExecutionContext context = GetContext();
|
||||
|
||||
return (uint)(context.Fpsr & FPSR.A32Mask & ~FPSR.Nzcv) |
|
||||
(uint)(context.Fpcr & FPCR.A32Mask);
|
||||
}
|
||||
|
||||
public static ulong GetTpidrEl0()
|
||||
{
|
||||
return (ulong)GetContext().TpidrEl0;
|
||||
@@ -130,29 +107,6 @@ namespace ARMeilleure.Instructions
|
||||
return GetContext().CntvctEl0;
|
||||
}
|
||||
|
||||
public static void SetFpcr(ulong value)
|
||||
{
|
||||
GetContext().Fpcr = (FPCR)value;
|
||||
}
|
||||
|
||||
public static void SetFpsr(ulong value)
|
||||
{
|
||||
GetContext().Fpsr = (FPSR)value;
|
||||
}
|
||||
|
||||
public static void SetFpsrQc()
|
||||
{
|
||||
GetContext().Fpsr |= FPSR.Qc;
|
||||
}
|
||||
|
||||
public static void SetFpscr(uint fpscr)
|
||||
{
|
||||
ExecutionContext context = GetContext();
|
||||
|
||||
context.Fpsr = FPSR.A32Mask & (FPSR)fpscr;
|
||||
context.Fpcr = FPCR.A32Mask & (FPCR)fpscr;
|
||||
}
|
||||
|
||||
public static void SetTpidrEl0(ulong value)
|
||||
{
|
||||
GetContext().TpidrEl0 = (long)value;
|
||||
|
@@ -91,76 +91,6 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Rounding"
|
||||
public static double Round(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
|
||||
FPRoundingMode roundMode = context.Fpcr.GetRoundingMode();
|
||||
|
||||
if (roundMode == FPRoundingMode.ToNearest)
|
||||
{
|
||||
return Math.Round(value); // even
|
||||
}
|
||||
else if (roundMode == FPRoundingMode.TowardsPlusInfinity)
|
||||
{
|
||||
return Math.Ceiling(value);
|
||||
}
|
||||
else if (roundMode == FPRoundingMode.TowardsMinusInfinity)
|
||||
{
|
||||
return Math.Floor(value);
|
||||
}
|
||||
else /* if (roundMode == FPRoundingMode.TowardsZero) */
|
||||
{
|
||||
return Math.Truncate(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static float RoundF(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
|
||||
FPRoundingMode roundMode = context.Fpcr.GetRoundingMode();
|
||||
|
||||
if (roundMode == FPRoundingMode.ToNearest)
|
||||
{
|
||||
return MathF.Round(value); // even
|
||||
}
|
||||
else if (roundMode == FPRoundingMode.TowardsPlusInfinity)
|
||||
{
|
||||
return MathF.Ceiling(value);
|
||||
}
|
||||
else if (roundMode == FPRoundingMode.TowardsMinusInfinity)
|
||||
{
|
||||
return MathF.Floor(value);
|
||||
}
|
||||
else /* if (roundMode == FPRoundingMode.TowardsZero) */
|
||||
{
|
||||
return MathF.Truncate(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static int FloatToInt32(float value)
|
||||
{
|
||||
return SatF32ToS32(RoundF(value));
|
||||
}
|
||||
|
||||
public static int DoubleToInt32(double value)
|
||||
{
|
||||
return SatF64ToS32(Round(value));
|
||||
}
|
||||
|
||||
public static uint FloatToUInt32(float value)
|
||||
{
|
||||
return SatF32ToU32(RoundF(value));
|
||||
}
|
||||
|
||||
public static uint DoubleToUInt32(double value)
|
||||
{
|
||||
return SatF64ToU32(Round(value));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Saturation"
|
||||
public static int SatF32ToS32(float value)
|
||||
{
|
||||
|
@@ -12,8 +12,8 @@ namespace ARMeilleure.Instructions
|
||||
RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable();
|
||||
}
|
||||
|
||||
internal static readonly byte[] RecipEstimateTable;
|
||||
internal static readonly byte[] RecipSqrtEstimateTable;
|
||||
public static readonly byte[] RecipEstimateTable;
|
||||
public static readonly byte[] RecipSqrtEstimateTable;
|
||||
|
||||
private static byte[] BuildRecipEstimateTable()
|
||||
{
|
||||
@@ -94,6 +94,13 @@ namespace ARMeilleure.Instructions
|
||||
context.Fpsr |= (FPSR)(1 << (int)exc);
|
||||
}
|
||||
}
|
||||
|
||||
public static FPRoundingMode GetRoundingMode(this FPCR fpcr)
|
||||
{
|
||||
const int RModeShift = 22;
|
||||
|
||||
return (FPRoundingMode)(((uint)fpcr >> RModeShift) & 3u);
|
||||
}
|
||||
}
|
||||
|
||||
static class SoftFloat16
|
||||
|
@@ -36,10 +36,25 @@ namespace ARMeilleure.State
|
||||
set => _nativeContext.SetPstate(value);
|
||||
}
|
||||
|
||||
public FPCR Fpcr { get; set; }
|
||||
public FPSR Fpsr { get; set; }
|
||||
public FPSR Fpsr
|
||||
{
|
||||
get => (FPSR)_nativeContext.GetFPState((uint)FPSR.Mask);
|
||||
set => _nativeContext.SetFPState((uint)value, (uint)FPSR.Mask);
|
||||
}
|
||||
|
||||
public FPCR Fpcr
|
||||
{
|
||||
get => (FPCR)_nativeContext.GetFPState((uint)FPCR.Mask);
|
||||
set => _nativeContext.SetFPState((uint)value, (uint)FPCR.Mask);
|
||||
}
|
||||
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
|
||||
|
||||
public FPSCR Fpscr
|
||||
{
|
||||
get => (FPSCR)_nativeContext.GetFPState((uint)FPSCR.Mask);
|
||||
set => _nativeContext.SetFPState((uint)value, (uint)FPSCR.Mask);
|
||||
}
|
||||
|
||||
public bool IsAarch32 { get; set; }
|
||||
|
||||
internal ExecutionMode ExecutionMode
|
||||
|
@@ -5,21 +5,18 @@ namespace ARMeilleure.State
|
||||
[Flags]
|
||||
public enum FPCR : uint
|
||||
{
|
||||
Ioe = 1u << 8,
|
||||
Dze = 1u << 9,
|
||||
Ofe = 1u << 10,
|
||||
Ufe = 1u << 11,
|
||||
Ixe = 1u << 12,
|
||||
Ide = 1u << 15,
|
||||
RMode0 = 1u << 22,
|
||||
RMode1 = 1u << 23,
|
||||
Fz = 1u << 24,
|
||||
Dn = 1u << 25,
|
||||
Ahp = 1u << 26,
|
||||
|
||||
A32Mask = 0x07FF9F00u
|
||||
}
|
||||
|
||||
public static class FPCRExtensions
|
||||
{
|
||||
private const int RModeShift = 22;
|
||||
|
||||
public static FPRoundingMode GetRoundingMode(this FPCR fpcr)
|
||||
{
|
||||
return (FPRoundingMode)(((int)fpcr >> RModeShift) & 3);
|
||||
}
|
||||
Mask = Ahp | Dn | Fz | RMode1 | RMode0 | Ide | Ixe | Ufe | Ofe | Dze | Ioe // 0x07C09F00u
|
||||
}
|
||||
}
|
||||
|
15
ARMeilleure/State/FPSCR.cs
Normal file
15
ARMeilleure/State/FPSCR.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
[Flags]
|
||||
public enum FPSCR : uint
|
||||
{
|
||||
V = 1u << 28,
|
||||
C = 1u << 29,
|
||||
Z = 1u << 30,
|
||||
N = 1u << 31,
|
||||
|
||||
Mask = N | Z | C | V | FPSR.Mask | FPCR.Mask // 0xFFC09F9Fu
|
||||
}
|
||||
}
|
@@ -5,11 +5,14 @@ namespace ARMeilleure.State
|
||||
[Flags]
|
||||
public enum FPSR : uint
|
||||
{
|
||||
Ioc = 1u << 0,
|
||||
Dzc = 1u << 1,
|
||||
Ofc = 1u << 2,
|
||||
Ufc = 1u << 3,
|
||||
Ixc = 1u << 4,
|
||||
Idc = 1u << 7,
|
||||
Qc = 1u << 27,
|
||||
|
||||
Nzcv = (1u << 31) | (1u << 30) | (1u << 29) | (1u << 28),
|
||||
|
||||
A32Mask = 0xF800009Fu
|
||||
Mask = Qc | Idc | Ixc | Ufc | Ofc | Dzc | Ioc // 0x0800009Fu
|
||||
}
|
||||
}
|
||||
|
@@ -2,9 +2,30 @@
|
||||
{
|
||||
public enum FPState
|
||||
{
|
||||
// FPSR Flags.
|
||||
IocFlag = 0,
|
||||
DzcFlag = 1,
|
||||
OfcFlag = 2,
|
||||
UfcFlag = 3,
|
||||
IxcFlag = 4,
|
||||
IdcFlag = 7,
|
||||
QcFlag = 27,
|
||||
VFlag = 28,
|
||||
CFlag = 29,
|
||||
ZFlag = 30,
|
||||
NFlag = 31
|
||||
NFlag = 31,
|
||||
|
||||
// FPCR Flags.
|
||||
IoeFlag = 8,
|
||||
DzeFlag = 9,
|
||||
OfeFlag = 10,
|
||||
UfeFlag = 11,
|
||||
IxeFlag = 12,
|
||||
IdeFlag = 15,
|
||||
RMode0Flag = 22,
|
||||
RMode1Flag = 23,
|
||||
FzFlag = 24,
|
||||
DnFlag = 25,
|
||||
AhpFlag = 26
|
||||
}
|
||||
}
|
||||
|
@@ -140,6 +140,34 @@ namespace ARMeilleure.State
|
||||
GetStorage().FpFlags[(int)flag] = value ? 1u : 0u;
|
||||
}
|
||||
|
||||
public unsafe uint GetFPState(uint mask = uint.MaxValue)
|
||||
{
|
||||
uint value = 0;
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
uint bit = 1u << flag;
|
||||
|
||||
if ((mask & bit) == bit)
|
||||
{
|
||||
value |= GetStorage().FpFlags[flag] != 0 ? bit : 0u;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe void SetFPState(uint value, uint mask = uint.MaxValue)
|
||||
{
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
uint bit = 1u << flag;
|
||||
|
||||
if ((mask & bit) == bit)
|
||||
{
|
||||
GetStorage().FpFlags[flag] = (value & bit) == bit ? 1u : 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetCounter() => GetStorage().Counter;
|
||||
public void SetCounter(int value) => GetStorage().Counter = value;
|
||||
|
||||
|
@@ -109,10 +109,6 @@ namespace ARMeilleure.Translation
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
||||
@@ -124,10 +120,6 @@ namespace ARMeilleure.Translation
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
||||
@@ -151,12 +143,8 @@ namespace ARMeilleure.Translation
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
|
||||
@@ -165,8 +153,6 @@ namespace ARMeilleure.Translation
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
|
||||
|
@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 3700; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 3703; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
@@ -879,7 +879,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||
Device.EnableDeviceVsync,
|
||||
Device.GetVolume(),
|
||||
LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%",
|
||||
Renderer.IsVulkan ? "Vulkan" : "OpenGL",
|
||||
dockedMode,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||
|
@@ -588,5 +588,6 @@
|
||||
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
|
||||
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
|
||||
"SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied",
|
||||
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?"
|
||||
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
|
||||
"VolumeShort": "Vol"
|
||||
}
|
||||
|
@@ -54,6 +54,12 @@
|
||||
<Style Selector="Border.huge">
|
||||
<Setter Property="Width" Value="200" />
|
||||
</Style>
|
||||
<Style Selector="Border.settings">
|
||||
<Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
<Setter Property="CornerRadius" Value="3" />
|
||||
</Style>
|
||||
<Style Selector="Image.small">
|
||||
<Setter Property="Width" Value="50" />
|
||||
</Style>
|
||||
@@ -193,6 +199,14 @@
|
||||
<Setter Property="Margin" Value="{DynamicResource TextMargin}" />
|
||||
<Setter Property="FontSize" Value="{DynamicResource FontSize}" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
|
||||
</Style>
|
||||
<Style Selector="TextBlock.h1">
|
||||
<Setter Property="Margin" Value="{DynamicResource TextMargin}" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
|
||||
</Style>
|
||||
<Style Selector="Separator">
|
||||
<Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
|
||||
|
@@ -5,7 +5,7 @@ namespace Ryujinx.Ava.Ui.Models
|
||||
internal class StatusUpdatedEventArgs : EventArgs
|
||||
{
|
||||
public bool VSyncEnabled { get; }
|
||||
public float Volume { get; }
|
||||
public string VolumeStatus { get; }
|
||||
public string GpuBackend { get; }
|
||||
public string AspectRatio { get; }
|
||||
public string DockedMode { get; }
|
||||
@@ -13,10 +13,10 @@ namespace Ryujinx.Ava.Ui.Models
|
||||
public string GameStatus { get; }
|
||||
public string GpuName { get; }
|
||||
|
||||
public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName)
|
||||
public StatusUpdatedEventArgs(bool vSyncEnabled, string volumeStatus, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName)
|
||||
{
|
||||
VSyncEnabled = vSyncEnabled;
|
||||
Volume = volume;
|
||||
VolumeStatus = volumeStatus;
|
||||
GpuBackend = gpuBackend;
|
||||
DockedMode = dockedMode;
|
||||
AspectRatio = aspectRatio;
|
||||
|
@@ -43,6 +43,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
private PlayerIndex _playerId;
|
||||
private int _controller;
|
||||
private int _controllerNumber = 0;
|
||||
private string _controllerImage;
|
||||
private int _device;
|
||||
private object _configuration;
|
||||
@@ -439,6 +440,14 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
return str;
|
||||
}
|
||||
|
||||
private static string GetShortGamepadId(string str)
|
||||
{
|
||||
const string Hyphen = "-";
|
||||
const int Offset = 1;
|
||||
|
||||
return str.Substring(str.IndexOf(Hyphen) + Offset);
|
||||
}
|
||||
|
||||
public void LoadDevices()
|
||||
{
|
||||
lock (Devices)
|
||||
@@ -451,9 +460,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
||||
|
||||
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
|
||||
|
||||
if (gamepad != null)
|
||||
{
|
||||
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)} ({id})"));
|
||||
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,12 +472,21 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
|
||||
|
||||
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
|
||||
|
||||
if (gamepad != null)
|
||||
{
|
||||
Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({id})"));
|
||||
if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
|
||||
{
|
||||
_controllerNumber++;
|
||||
}
|
||||
|
||||
Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({_controllerNumber})"));
|
||||
}
|
||||
}
|
||||
|
||||
_controllerNumber = 0;
|
||||
|
||||
DeviceList.AddRange(Devices.Select(x => x.Name));
|
||||
Device = Math.Min(Device, DeviceList.Count);
|
||||
}
|
||||
|
@@ -50,6 +50,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
private string _dockedStatusText;
|
||||
private string _fifoStatusText;
|
||||
private string _gameStatusText;
|
||||
private string _volumeStatusText;
|
||||
private string _gpuStatusText;
|
||||
private bool _isAmiiboRequested;
|
||||
private bool _isGameRunning;
|
||||
@@ -385,11 +386,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
public string VolumeStatusText
|
||||
{
|
||||
get
|
||||
get => _volumeStatusText;
|
||||
set
|
||||
{
|
||||
string icon = Volume == 0 ? "🔇" : "🔊";
|
||||
_volumeStatusText = value;
|
||||
|
||||
return $"{icon} {(int)(Volume * 100)}%";
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,8 @@
|
||||
Grid.Column="0"
|
||||
Margin="0,0,2,0"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1">
|
||||
BorderThickness="1"
|
||||
Padding="2,0">
|
||||
<StackPanel
|
||||
Margin="2"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -65,7 +66,8 @@
|
||||
Grid.Column="1"
|
||||
Margin="0,0,2,0"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1">
|
||||
BorderThickness="1"
|
||||
Padding="2,0">
|
||||
<StackPanel
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -103,7 +105,8 @@
|
||||
Grid.Column="2"
|
||||
Margin="0,0,2,0"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1">
|
||||
BorderThickness="1"
|
||||
Padding="2,0">
|
||||
<Grid
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -132,7 +135,8 @@
|
||||
<Border
|
||||
Grid.Column="3"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1">
|
||||
BorderThickness="1"
|
||||
Padding="2,0" >
|
||||
<StackPanel
|
||||
Margin="2"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -151,7 +155,7 @@
|
||||
Items="{Binding ProfilesList}"
|
||||
Text="{Binding ProfileName}" />
|
||||
<Button
|
||||
MinWidth="60"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}"
|
||||
@@ -162,7 +166,7 @@
|
||||
Height="20" />
|
||||
</Button>
|
||||
<Button
|
||||
MinWidth="60"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}"
|
||||
@@ -173,7 +177,7 @@
|
||||
Height="20" />
|
||||
</Button>
|
||||
<Button
|
||||
MinWidth="60"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}"
|
||||
@@ -717,7 +721,7 @@
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{Binding Configuration.EnableMotion, Mode=TwoWay}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsMotion}" TextWrapping="WrapWithOverflow" />
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsMotion}" />
|
||||
</CheckBox>
|
||||
<Button Margin="10" Grid.Column="1" Command="{Binding ShowMotionConfig}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" />
|
||||
@@ -739,7 +743,7 @@
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{Binding Configuration.EnableRumble, Mode=TwoWay}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsRumble}" TextWrapping="WrapWithOverflow" />
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsRumble}" />
|
||||
</CheckBox>
|
||||
<Button Margin="10" Grid.Column="1" Command="{Binding ShowRumbleConfig}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" />
|
||||
@@ -780,8 +784,6 @@
|
||||
Margin="0,0,0,4"
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="{DynamicResource ThemeDarkColor}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
@@ -822,7 +824,7 @@
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Margin="0,0,8,4"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Right"
|
||||
@@ -957,7 +959,6 @@
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Margin="2,0,2,2"
|
||||
Padding="10"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
|
@@ -662,12 +662,12 @@
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<ui:ToggleSplitButton
|
||||
Name="VolumeStatus"
|
||||
Margin="-2,0,-3,0"
|
||||
Padding="5,0,0,5"
|
||||
Padding="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
BorderBrush="{DynamicResource ThemeContentBackgroundColor}"
|
||||
BorderThickness="0"
|
||||
Content="{Binding VolumeStatusText}"
|
||||
IsChecked="{Binding VolumeMuted}"
|
||||
IsVisible="{Binding !ShowLoadProgress}">
|
||||
|
@@ -138,6 +138,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
ViewModel.DockedStatusText = args.DockedMode;
|
||||
ViewModel.AspectRatioStatusText = args.AspectRatio;
|
||||
ViewModel.GameStatusText = args.GameStatus;
|
||||
ViewModel.VolumeStatusText = args.VolumeStatus;
|
||||
ViewModel.FifoStatusText = args.FifoStatus;
|
||||
ViewModel.GpuNameText = args.GpuName;
|
||||
ViewModel.BackendText = args.GpuBackend;
|
||||
|
@@ -38,18 +38,18 @@
|
||||
KeyboardNavigation.IsTabStop="False"/>
|
||||
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
|
||||
<ScrollViewer Name="UiPage"
|
||||
Margin="0,0,10,0"
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralGeneral}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
@@ -67,7 +67,7 @@
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -111,7 +111,7 @@
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralTheme}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -169,7 +169,7 @@
|
||||
Padding="0,0,2,0"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="4" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox Margin="5,0"
|
||||
@@ -198,9 +198,9 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="10,5" Orientation="Vertical" Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
@@ -265,13 +265,13 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabSystemCore}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
@@ -384,6 +384,7 @@
|
||||
<AutoCompleteBox
|
||||
Name="TimeZoneBox"
|
||||
Width="350"
|
||||
MaxDropDownHeight="500"
|
||||
FilterMode="Contains"
|
||||
Items="{Binding TimeZones}"
|
||||
SelectionChanged="TimeZoneBox_OnSelectionChanged"
|
||||
@@ -420,7 +421,7 @@
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabSystemHacks}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
@@ -445,13 +446,13 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabCpuCache}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -462,7 +463,7 @@
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabCpuMemory}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -502,13 +503,13 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsAPI}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
@@ -538,8 +539,10 @@
|
||||
SelectedIndex="{Binding PreferredGpuIndex}"
|
||||
Items="{Binding AvailableGpus}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableShaderCache}"
|
||||
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
|
||||
@@ -673,7 +676,7 @@
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -698,13 +701,13 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabAudio}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemAudioBackend}"
|
||||
@@ -763,13 +766,13 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabNetworkConnection}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
|
||||
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
||||
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
||||
@@ -783,13 +786,13 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border>
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabLoggingLogging}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableFileLog}"
|
||||
ToolTip.Tip="{locale:Locale FileLogTooltip}">
|
||||
@@ -821,7 +824,7 @@
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -880,10 +883,15 @@
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<ui:NavigationView Grid.Row="1" IsSettingsVisible="False" Name="NavPanel" IsBackEnabled="False"
|
||||
PaneDisplayMode="LeftCompact"
|
||||
<ui:NavigationView Grid.Row="1"
|
||||
IsSettingsVisible="False"
|
||||
Name="NavPanel"
|
||||
IsBackEnabled="False"
|
||||
PaneDisplayMode="Left"
|
||||
Margin="2,10,10,0"
|
||||
VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
OpenPaneLength="200">
|
||||
<ui:NavigationView.MenuItems>
|
||||
<ui:NavigationViewItem IsSelected="True"
|
||||
Content="{locale:Locale SettingsTabGeneral}"
|
||||
|
@@ -806,7 +806,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
||||
|
||||
int handle = _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4);
|
||||
int handle = textureBufferAddress != 0
|
||||
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
|
||||
: 0;
|
||||
|
||||
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
||||
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
|
||||
@@ -824,7 +826,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
||||
|
||||
samplerHandle = _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4);
|
||||
samplerHandle = samplerBufferAddress != 0
|
||||
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
|
||||
: 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 3697;
|
||||
private const uint CodeGenVersion = 3728;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@@ -1,98 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
class IntermmediatePool : IDisposable
|
||||
{
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
private readonly List<TextureView> _entries;
|
||||
|
||||
public IntermmediatePool(OpenGLRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
_entries = new List<TextureView>();
|
||||
}
|
||||
|
||||
public TextureView GetOrCreateWithAtLeast(
|
||||
Target target,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel,
|
||||
Format format,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int levels)
|
||||
{
|
||||
TextureView entry;
|
||||
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
entry = _entries[i];
|
||||
|
||||
if (entry.Target == target && entry.Format == format)
|
||||
{
|
||||
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
|
||||
{
|
||||
width = Math.Max(width, entry.Width);
|
||||
height = Math.Max(height, entry.Height);
|
||||
depth = Math.Max(depth, entry.Info.Depth);
|
||||
levels = Math.Max(levels, entry.Info.Levels);
|
||||
|
||||
entry.Dispose();
|
||||
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
|
||||
_entries[i] = entry;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
|
||||
_entries.Add(entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private TextureView CreateNew(
|
||||
Target target,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel,
|
||||
Format format,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int levels)
|
||||
{
|
||||
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
levels,
|
||||
1,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
bytesPerPixel,
|
||||
format,
|
||||
DepthStencilMode.Depth,
|
||||
target,
|
||||
SwizzleComponent.Red,
|
||||
SwizzleComponent.Green,
|
||||
SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha), 1f);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (TextureView entry in _entries)
|
||||
{
|
||||
entry.Dispose();
|
||||
}
|
||||
|
||||
_entries.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,8 +9,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
|
||||
public IntermmediatePool IntermmediatePool { get; }
|
||||
|
||||
private int _srcFramebuffer;
|
||||
private int _dstFramebuffer;
|
||||
|
||||
@@ -20,7 +18,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
public TextureCopy(OpenGLRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
IntermmediatePool = new IntermmediatePool(renderer);
|
||||
}
|
||||
|
||||
public void Copy(
|
||||
@@ -517,8 +514,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
|
||||
_copyPboHandle = 0;
|
||||
}
|
||||
|
||||
IntermmediatePool.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
276
Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs
Normal file
276
Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
class TextureCopyMS
|
||||
{
|
||||
private const string ComputeShaderMSToNonMS = @"#version 450 core
|
||||
|
||||
layout (binding = 0, $FORMAT$) uniform uimage2DMS imgIn;
|
||||
layout (binding = 1, $FORMAT$) uniform uimage2D imgOut;
|
||||
|
||||
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 coords = gl_GlobalInvocationID.xy;
|
||||
ivec2 imageSz = imageSize(imgOut);
|
||||
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int inSamples = imageSamples(imgIn);
|
||||
int samplesInXLog2 = 0;
|
||||
int samplesInYLog2 = 0;
|
||||
switch (inSamples)
|
||||
{
|
||||
case 2:
|
||||
samplesInXLog2 = 1;
|
||||
break;
|
||||
case 4:
|
||||
samplesInXLog2 = 1;
|
||||
samplesInYLog2 = 1;
|
||||
break;
|
||||
case 8:
|
||||
samplesInXLog2 = 2;
|
||||
samplesInYLog2 = 1;
|
||||
break;
|
||||
case 16:
|
||||
samplesInXLog2 = 2;
|
||||
samplesInYLog2 = 2;
|
||||
break;
|
||||
}
|
||||
int samplesInX = 1 << samplesInXLog2;
|
||||
int samplesInY = 1 << samplesInYLog2;
|
||||
int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
|
||||
uvec4 value = imageLoad(imgIn, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx);
|
||||
imageStore(imgOut, ivec2(coords), value);
|
||||
}";
|
||||
|
||||
private const string ComputeShaderNonMSToMS = @"#version 450 core
|
||||
|
||||
layout (binding = 0, $FORMAT$) uniform uimage2D imgIn;
|
||||
layout (binding = 1, $FORMAT$) uniform uimage2DMS imgOut;
|
||||
|
||||
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 coords = gl_GlobalInvocationID.xy;
|
||||
ivec2 imageSz = imageSize(imgIn);
|
||||
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int outSamples = imageSamples(imgOut);
|
||||
int samplesInXLog2 = 0;
|
||||
int samplesInYLog2 = 0;
|
||||
switch (outSamples)
|
||||
{
|
||||
case 2:
|
||||
samplesInXLog2 = 1;
|
||||
break;
|
||||
case 4:
|
||||
samplesInXLog2 = 1;
|
||||
samplesInYLog2 = 1;
|
||||
break;
|
||||
case 8:
|
||||
samplesInXLog2 = 2;
|
||||
samplesInYLog2 = 1;
|
||||
break;
|
||||
case 16:
|
||||
samplesInXLog2 = 2;
|
||||
samplesInYLog2 = 2;
|
||||
break;
|
||||
}
|
||||
int samplesInX = 1 << samplesInXLog2;
|
||||
int samplesInY = 1 << samplesInYLog2;
|
||||
int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
|
||||
uvec4 value = imageLoad(imgIn, ivec2(coords));
|
||||
imageStore(imgOut, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx, value);
|
||||
}";
|
||||
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
private int[] _msToNonMSProgramHandles;
|
||||
private int[] _nonMSToMSProgramHandles;
|
||||
|
||||
public TextureCopyMS(OpenGLRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
_msToNonMSProgramHandles = new int[5];
|
||||
_nonMSToMSProgramHandles = new int[5];
|
||||
}
|
||||
|
||||
public void CopyMSToNonMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
|
||||
{
|
||||
TextureCreateInfo srcInfo = src.Info;
|
||||
TextureCreateInfo dstInfo = dst.Info;
|
||||
|
||||
int srcHandle = CreateViewIfNeeded(src);
|
||||
int dstHandle = CreateViewIfNeeded(dst);
|
||||
|
||||
int dstWidth = dstInfo.Width;
|
||||
int dstHeight = dstInfo.Height;
|
||||
|
||||
GL.UseProgram(GetMSToNonMSShader(srcInfo.BytesPerPixel));
|
||||
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
|
||||
GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
|
||||
|
||||
GL.DispatchCompute((dstWidth + 31) / 32, (dstHeight + 31) / 32, 1);
|
||||
}
|
||||
|
||||
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
|
||||
|
||||
pipeline.RestoreProgram();
|
||||
pipeline.RestoreImages1And2();
|
||||
|
||||
DestroyViewIfNeeded(src, srcHandle);
|
||||
DestroyViewIfNeeded(dst, dstHandle);
|
||||
}
|
||||
|
||||
public void CopyNonMSToMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
|
||||
{
|
||||
TextureCreateInfo srcInfo = src.Info;
|
||||
TextureCreateInfo dstInfo = dst.Info;
|
||||
|
||||
int srcHandle = CreateViewIfNeeded(src);
|
||||
int dstHandle = CreateViewIfNeeded(dst);
|
||||
|
||||
int srcWidth = srcInfo.Width;
|
||||
int srcHeight = srcInfo.Height;
|
||||
|
||||
GL.UseProgram(GetNonMSToMSShader(srcInfo.BytesPerPixel));
|
||||
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
|
||||
GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
|
||||
|
||||
GL.DispatchCompute((srcWidth + 31) / 32, (srcHeight + 31) / 32, 1);
|
||||
}
|
||||
|
||||
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
|
||||
|
||||
pipeline.RestoreProgram();
|
||||
pipeline.RestoreImages1And2();
|
||||
|
||||
DestroyViewIfNeeded(src, srcHandle);
|
||||
DestroyViewIfNeeded(dst, dstHandle);
|
||||
}
|
||||
|
||||
private static SizedInternalFormat GetFormat(int bytesPerPixel)
|
||||
{
|
||||
return bytesPerPixel switch
|
||||
{
|
||||
1 => SizedInternalFormat.R8ui,
|
||||
2 => SizedInternalFormat.R16ui,
|
||||
4 => SizedInternalFormat.R32ui,
|
||||
8 => SizedInternalFormat.Rg32ui,
|
||||
16 => SizedInternalFormat.Rgba32ui,
|
||||
_ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
|
||||
};
|
||||
}
|
||||
|
||||
private static int CreateViewIfNeeded(ITextureInfo texture)
|
||||
{
|
||||
// Binding sRGB textures as images doesn't work on NVIDIA,
|
||||
// we need to create and bind a RGBA view for it to work.
|
||||
if (texture.Info.Format == Format.R8G8B8A8Srgb)
|
||||
{
|
||||
int handle = GL.GenTexture();
|
||||
|
||||
GL.TextureView(
|
||||
handle,
|
||||
texture.Info.Target.Convert(),
|
||||
texture.Storage.Handle,
|
||||
PixelInternalFormat.Rgba8,
|
||||
texture.FirstLevel,
|
||||
1,
|
||||
texture.FirstLayer,
|
||||
texture.Info.GetLayers());
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
return texture.Handle;
|
||||
}
|
||||
|
||||
private static void DestroyViewIfNeeded(ITextureInfo info, int handle)
|
||||
{
|
||||
if (info.Handle != handle)
|
||||
{
|
||||
GL.DeleteTexture(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetMSToNonMSShader(int bytesPerPixel)
|
||||
{
|
||||
return GetShader(ComputeShaderMSToNonMS, _msToNonMSProgramHandles, bytesPerPixel);
|
||||
}
|
||||
|
||||
private int GetNonMSToMSShader(int bytesPerPixel)
|
||||
{
|
||||
return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel);
|
||||
}
|
||||
|
||||
private int GetShader(string code, int[] programHandles, int bytesPerPixel)
|
||||
{
|
||||
int index = BitOperations.Log2((uint)bytesPerPixel);
|
||||
|
||||
if (programHandles[index] == 0)
|
||||
{
|
||||
int csHandle = GL.CreateShader(ShaderType.ComputeShader);
|
||||
|
||||
string format = new[] { "r8ui", "r16ui", "r32ui", "rg32ui", "rgba32ui" }[index];
|
||||
|
||||
GL.ShaderSource(csHandle, code.Replace("$FORMAT$", format));
|
||||
GL.CompileShader(csHandle);
|
||||
|
||||
int programHandle = GL.CreateProgram();
|
||||
|
||||
GL.AttachShader(programHandle, csHandle);
|
||||
GL.LinkProgram(programHandle);
|
||||
GL.DetachShader(programHandle, csHandle);
|
||||
GL.DeleteShader(csHandle);
|
||||
|
||||
GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
throw new Exception(GL.GetProgramInfoLog(programHandle));
|
||||
}
|
||||
|
||||
programHandles[index] = programHandle;
|
||||
}
|
||||
|
||||
return programHandles[index];
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (int i = 0; i < _msToNonMSProgramHandles.Length; i++)
|
||||
{
|
||||
if (_msToNonMSProgramHandles[i] != 0)
|
||||
{
|
||||
GL.DeleteProgram(_msToNonMSProgramHandles[i]);
|
||||
_msToNonMSProgramHandles[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _nonMSToMSProgramHandles.Length; i++)
|
||||
{
|
||||
if (_nonMSToMSProgramHandles[i] != 0)
|
||||
{
|
||||
GL.DeleteProgram(_nonMSToMSProgramHandles[i]);
|
||||
_nonMSToMSProgramHandles[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -116,28 +116,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
TextureView destinationView = (TextureView)destination;
|
||||
|
||||
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
|
||||
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
||||
{
|
||||
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
||||
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
||||
|
||||
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
|
||||
GetIntermmediateTarget(Target),
|
||||
Info.BlockWidth,
|
||||
Info.BlockHeight,
|
||||
Info.BytesPerPixel,
|
||||
Format,
|
||||
Width,
|
||||
Height,
|
||||
Info.Depth,
|
||||
Info.Levels);
|
||||
|
||||
GL.Disable(EnableCap.FramebufferSrgb);
|
||||
|
||||
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
|
||||
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
|
||||
|
||||
GL.Enable(EnableCap.FramebufferSrgb);
|
||||
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
|
||||
}
|
||||
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
||||
{
|
||||
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
|
||||
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -149,28 +136,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
TextureView destinationView = (TextureView)destination;
|
||||
|
||||
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
|
||||
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
||||
{
|
||||
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
||||
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
||||
|
||||
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
|
||||
GetIntermmediateTarget(Target),
|
||||
Info.BlockWidth,
|
||||
Info.BlockHeight,
|
||||
Info.BytesPerPixel,
|
||||
Format,
|
||||
Math.Max(1, Width >> srcLevel),
|
||||
Math.Max(1, Height >> srcLevel),
|
||||
1,
|
||||
1);
|
||||
|
||||
GL.Disable(EnableCap.FramebufferSrgb);
|
||||
|
||||
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
|
||||
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
|
||||
|
||||
GL.Enable(EnableCap.FramebufferSrgb);
|
||||
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1);
|
||||
}
|
||||
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
||||
{
|
||||
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -178,17 +150,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
}
|
||||
}
|
||||
|
||||
private static Target GetIntermmediateTarget(Target srcTarget)
|
||||
{
|
||||
return srcTarget switch
|
||||
{
|
||||
Target.Texture2D => Target.Texture2DMultisample,
|
||||
Target.Texture2DArray => Target.Texture2DMultisampleArray,
|
||||
Target.Texture2DMultisampleArray => Target.Texture2DArray,
|
||||
_ => Target.Texture2D
|
||||
};
|
||||
}
|
||||
|
||||
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
||||
{
|
||||
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
||||
|
@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
private TextureCopy _textureCopy;
|
||||
private TextureCopy _backgroundTextureCopy;
|
||||
internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
|
||||
internal TextureCopyMS TextureCopyMS { get; }
|
||||
|
||||
private Sync _sync;
|
||||
|
||||
@@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_window = new Window(this);
|
||||
_textureCopy = new TextureCopy(this);
|
||||
_backgroundTextureCopy = new TextureCopy(this);
|
||||
TextureCopyMS = new TextureCopyMS(this);
|
||||
_sync = new Sync();
|
||||
PersistentBuffers = new PersistentBuffers();
|
||||
ResourcePool = new ResourcePool();
|
||||
@@ -211,6 +213,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
_textureCopy.Dispose();
|
||||
_backgroundTextureCopy.Dispose();
|
||||
TextureCopyMS.Dispose();
|
||||
PersistentBuffers.Dispose();
|
||||
ResourcePool.Dispose();
|
||||
_pipeline.Dispose();
|
||||
|
@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
class Pipeline : IPipeline, IDisposable
|
||||
{
|
||||
private const int SavedImages = 2;
|
||||
|
||||
private readonly DrawTextureEmulation _drawTexture;
|
||||
|
||||
internal ulong DrawCount { get; private set; }
|
||||
@@ -46,6 +48,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
private Vector4<float>[] _renderScale = new Vector4<float>[73];
|
||||
private int _fragmentScaleCount;
|
||||
|
||||
private (TextureBase, Format)[] _images;
|
||||
private TextureBase _unit0Texture;
|
||||
private Sampler _unit0Sampler;
|
||||
|
||||
@@ -78,6 +81,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_fragmentOutputMap = uint.MaxValue;
|
||||
_componentMasks = uint.MaxValue;
|
||||
|
||||
_images = new (TextureBase, Format)[SavedImages];
|
||||
|
||||
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
|
||||
new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
|
||||
|
||||
@@ -907,6 +912,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
public void SetImage(int binding, ITexture texture, Format imageFormat)
|
||||
{
|
||||
if ((uint)binding < SavedImages)
|
||||
{
|
||||
_images[binding] = (texture as TextureBase, imageFormat);
|
||||
}
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
return;
|
||||
@@ -1608,6 +1618,32 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
}
|
||||
}
|
||||
|
||||
public void RestoreProgram()
|
||||
{
|
||||
_program?.Bind();
|
||||
}
|
||||
|
||||
public void RestoreImages1And2()
|
||||
{
|
||||
for (int i = 0; i < SavedImages; i++)
|
||||
{
|
||||
(TextureBase texBase, Format imageFormat) = _images[i];
|
||||
|
||||
if (texBase != null)
|
||||
{
|
||||
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
|
||||
|
||||
if (format != 0)
|
||||
{
|
||||
GL.BindImageTexture(i, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
GL.BindImageTexture(i, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
||||
{
|
||||
if (value is CounterQueueEvent)
|
||||
|
@@ -2,6 +2,7 @@ using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
@@ -163,9 +164,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.TessellationEvaluation)
|
||||
{
|
||||
bool tessCw = context.Config.GpuAccessor.QueryTessCw();
|
||||
|
||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||
{
|
||||
// We invert the front face on Vulkan backend, so we need to do that here aswell.
|
||||
tessCw = !tessCw;
|
||||
}
|
||||
|
||||
string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl();
|
||||
string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl();
|
||||
string windingOrder = context.Config.GpuAccessor.QueryTessCw() ? "cw" : "ccw";
|
||||
string windingOrder = tessCw ? "cw" : "ccw";
|
||||
|
||||
context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;");
|
||||
context.AppendLine();
|
||||
@@ -185,14 +194,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
context.AppendLine();
|
||||
}
|
||||
|
||||
if (context.Config.UsedInputAttributesPerPatch != 0)
|
||||
if (context.Config.UsedInputAttributesPerPatch.Count != 0)
|
||||
{
|
||||
DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch);
|
||||
|
||||
context.AppendLine();
|
||||
}
|
||||
|
||||
if (context.Config.UsedOutputAttributesPerPatch != 0)
|
||||
if (context.Config.UsedOutputAttributesPerPatch.Count != 0)
|
||||
{
|
||||
DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch);
|
||||
|
||||
@@ -509,13 +518,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareInputAttributesPerPatch(CodeGenContext context, int usedAttributes)
|
||||
private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
||||
{
|
||||
while (usedAttributes != 0)
|
||||
foreach (int attr in attrs.OrderBy(x => x))
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
DeclareInputAttributePerPatch(context, index);
|
||||
usedAttributes &= ~(1 << index);
|
||||
DeclareInputAttributePerPatch(context, attr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,16 +573,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr)
|
||||
{
|
||||
string layout = string.Empty;
|
||||
|
||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||
{
|
||||
layout = $"layout (location = {32 + attr}) ";
|
||||
}
|
||||
|
||||
int location = context.Config.GetPerPatchAttributeLocation(attr);
|
||||
string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}";
|
||||
|
||||
context.AppendLine($"{layout}patch in vec4 {name};");
|
||||
context.AppendLine($"layout (location = {location}) patch in vec4 {name};");
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||
@@ -624,28 +625,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, int usedAttributes)
|
||||
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
||||
{
|
||||
while (usedAttributes != 0)
|
||||
foreach (int attr in attrs.OrderBy(x => x))
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
DeclareOutputAttributePerPatch(context, index);
|
||||
usedAttributes &= ~(1 << index);
|
||||
DeclareOutputAttributePerPatch(context, attr);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr)
|
||||
{
|
||||
string layout = string.Empty;
|
||||
|
||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||
{
|
||||
layout = $"layout (location = {32 + attr}) ";
|
||||
}
|
||||
|
||||
int location = context.Config.GetPerPatchAttributeLocation(attr);
|
||||
string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}";
|
||||
|
||||
context.AppendLine($"{layout}patch out vec4 {name};");
|
||||
context.AppendLine($"layout (location = {location}) patch out vec4 {name};");
|
||||
}
|
||||
|
||||
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
|
||||
|
@@ -28,12 +28,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
|
||||
{
|
||||
{ AttributeConsts.TessLevelOuter0, new BuiltInAttribute("gl_TessLevelOuter[0]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter1, new BuiltInAttribute("gl_TessLevelOuter[1]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter2, new BuiltInAttribute("gl_TessLevelOuter[2]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter3, new BuiltInAttribute("gl_TessLevelOuter[3]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelInner0, new BuiltInAttribute("gl_TessLevelInner[0]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelInner1, new BuiltInAttribute("gl_TessLevelInner[1]", VariableType.F32) },
|
||||
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
|
||||
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
|
||||
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
|
||||
@@ -170,7 +164,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
value &= AttributeConsts.Mask & ~3;
|
||||
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||
|
||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||
if (perPatch)
|
||||
{
|
||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
||||
{
|
||||
value -= AttributeConsts.UserAttributePerPatchBase;
|
||||
|
||||
return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}";
|
||||
}
|
||||
else if (value < AttributeConsts.UserAttributePerPatchBase)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]",
|
||||
AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]",
|
||||
AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]",
|
||||
AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]",
|
||||
AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]",
|
||||
AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
value -= AttributeConsts.UserAttributeBase;
|
||||
|
||||
@@ -180,11 +196,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing);
|
||||
|
||||
if (!indexable && perPatch)
|
||||
{
|
||||
prefix = DefaultNames.PerPatchAttributePrefix;
|
||||
}
|
||||
|
||||
if (indexable)
|
||||
{
|
||||
string name = prefix;
|
||||
@@ -202,7 +213,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{
|
||||
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
||||
|
||||
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
||||
{
|
||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
||||
}
|
||||
@@ -213,7 +224,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{
|
||||
string name = $"{prefix}{(value >> 4)}";
|
||||
|
||||
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
||||
{
|
||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
||||
}
|
||||
@@ -277,7 +288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
string name = builtInAttr.Name;
|
||||
|
||||
if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value))
|
||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value))
|
||||
{
|
||||
name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}";
|
||||
}
|
||||
|
@@ -382,17 +382,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)
|
||||
{
|
||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||
var attrInfo = AttributeInfo.From(Config, attr, isOutAttr);
|
||||
var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr);
|
||||
|
||||
int attrOffset = attrInfo.BaseValue;
|
||||
Instruction ioVariable;
|
||||
|
||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
||||
Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
|
||||
|
||||
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
||||
|
||||
ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
|
||||
|
||||
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
|
||||
{
|
||||
return ioVariable;
|
||||
@@ -404,7 +400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr)
|
||||
{
|
||||
if (!AttributeInfo.Validate(Config, attr, isOutAttr: false))
|
||||
if (!AttributeInfo.ValidatePerPatch(Config, attr, isOutAttr: false))
|
||||
{
|
||||
return GetConstant(type, new AstOperand(IrOperandType.Constant, 0));
|
||||
}
|
||||
|
@@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
foreach (int attr in inputs)
|
||||
{
|
||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false))
|
||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -459,7 +459,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
foreach (int attr in outputs)
|
||||
{
|
||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true))
|
||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -519,7 +519,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||
: (isOutAttr ? context.Outputs : context.Inputs);
|
||||
|
||||
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
|
||||
var attrInfo = perPatch
|
||||
? AttributeInfo.FromPatch(context.Config, attr, isOutAttr)
|
||||
: AttributeInfo.From(context.Config, attr, isOutAttr);
|
||||
|
||||
if (dict.ContainsKey(attrInfo.BaseValue))
|
||||
{
|
||||
@@ -544,11 +546,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
var spvType = context.TypePointer(storageClass, attrType);
|
||||
var spvVar = context.Variable(spvType, storageClass);
|
||||
|
||||
if (perPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
}
|
||||
|
||||
if (builtInPassthrough)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
@@ -556,6 +553,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
if (attrInfo.IsBuiltin)
|
||||
{
|
||||
if (perPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
}
|
||||
|
||||
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
|
||||
@@ -569,6 +571,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (perPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
|
||||
int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16);
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
else if (isUserAttr)
|
||||
{
|
||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
||||
|
@@ -882,7 +882,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||
{
|
||||
int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2);
|
||||
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr: false, index));
|
||||
bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0;
|
||||
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -191,7 +191,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
break;
|
||||
}
|
||||
|
||||
if (context.Config.GpuAccessor.QueryTessCw())
|
||||
bool tessCw = context.Config.GpuAccessor.QueryTessCw();
|
||||
|
||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||
{
|
||||
// We invert the front face on Vulkan backend, so we need to do that here aswell.
|
||||
tessCw = !tessCw;
|
||||
}
|
||||
|
||||
if (tessCw)
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw);
|
||||
}
|
||||
@@ -374,10 +382,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
context.Store(context.GetLocalPointer(dest), source);
|
||||
}
|
||||
else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch)
|
||||
{
|
||||
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true))
|
||||
{
|
||||
bool perPatch = dest.Type == OperandType.AttributePerPatch;
|
||||
|
||||
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch))
|
||||
{
|
||||
AggregateType elemType;
|
||||
|
||||
var elemPointer = perPatch
|
||||
|
@@ -306,18 +306,36 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
for (int elemIndex = 0; elemIndex < count; elemIndex++)
|
||||
{
|
||||
int attr = offset + elemIndex * 4;
|
||||
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
||||
|
||||
if (perPatch)
|
||||
{
|
||||
if (attr >= AttributeConsts.UserAttributePerPatchBase && attr < AttributeConsts.UserAttributePerPatchEnd)
|
||||
{
|
||||
int userAttr = attr - AttributeConsts.UserAttributePerPatchBase;
|
||||
int index = userAttr / 16;
|
||||
|
||||
if (isStore)
|
||||
{
|
||||
config.SetOutputUserAttributePerPatch(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.SetInputUserAttributePerPatch(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
int userAttr = attr - AttributeConsts.UserAttributeBase;
|
||||
int index = userAttr / 16;
|
||||
|
||||
if (isStore)
|
||||
{
|
||||
config.SetOutputUserAttribute(index, perPatch);
|
||||
config.SetOutputUserAttribute(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.SetInputUserAttribute(index, (userAttr >> 2) & 3, perPatch);
|
||||
config.SetInputUserAttribute(index, (userAttr >> 2) & 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
context.FlagAttributeRead(offset);
|
||||
|
||||
if (op.O)
|
||||
if (op.O && CanLoadOutput(offset))
|
||||
{
|
||||
offset |= AttributeConsts.LoadOutputMask;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
context.FlagAttributeRead(offset);
|
||||
|
||||
if (op.O)
|
||||
if (op.O && CanLoadOutput(offset))
|
||||
{
|
||||
offset |= AttributeConsts.LoadOutputMask;
|
||||
}
|
||||
@@ -241,6 +241,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanLoadOutput(int attr)
|
||||
{
|
||||
return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY;
|
||||
}
|
||||
|
||||
private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr)
|
||||
{
|
||||
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR)
|
||||
|
@@ -97,9 +97,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant)
|
||||
{
|
||||
int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2);
|
||||
|
||||
if ((src1.Value & AttributeConsts.LoadOutputMask) != 0)
|
||||
{
|
||||
context.Info.Outputs.Add(attrOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Info.Inputs.Add(attrOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sourcesCount = operation.SourcesCount;
|
||||
int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0;
|
||||
|
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset);
|
||||
|
||||
int handle = cachedTextureBuffer[textureWordOffset];
|
||||
int handle = cachedTextureBuffer.Length != 0 ? cachedTextureBuffer[textureWordOffset] : 0;
|
||||
|
||||
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
||||
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
|
||||
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
|
||||
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
||||
{
|
||||
samplerHandle = cachedSamplerBuffer[samplerWordOffset];
|
||||
samplerHandle = cachedSamplerBuffer.Length != 0 ? cachedSamplerBuffer[samplerWordOffset] : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -54,6 +54,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
public const int UserAttributeBase = 0x80;
|
||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
||||
|
||||
public const int UserAttributePerPatchBase = 0x18;
|
||||
public const int UserAttributePerPatchEnd = 0x200;
|
||||
|
||||
public const int LoadOutputMask = 1 << 30;
|
||||
public const int Mask = 0x3fffffff;
|
||||
|
||||
|
@@ -4,14 +4,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
struct AttributeInfo
|
||||
{
|
||||
private static readonly Dictionary<int, AttributeInfo> BuiltInAttributes = new Dictionary<int, AttributeInfo>()
|
||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
|
||||
{
|
||||
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
|
||||
@@ -29,8 +23,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
|
||||
@@ -55,6 +49,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | AggregateType.U32) },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
|
||||
{
|
||||
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
|
||||
};
|
||||
|
||||
public int BaseValue { get; }
|
||||
public int Value { get; }
|
||||
public int Length { get; }
|
||||
@@ -76,6 +80,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return (Value - BaseValue) / 4;
|
||||
}
|
||||
|
||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch)
|
||||
{
|
||||
return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr);
|
||||
}
|
||||
|
||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
|
||||
@@ -86,6 +95,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return From(config, value, isOutAttr).IsValid;
|
||||
}
|
||||
|
||||
public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
return FromPatch(config, value, isOutAttr).IsValid;
|
||||
}
|
||||
|
||||
public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
value &= ~3;
|
||||
@@ -115,7 +129,24 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
return new AttributeInfo(value, 0, 1, AggregateType.FP32);
|
||||
}
|
||||
else if (BuiltInAttributes.TryGetValue(value, out AttributeInfo info))
|
||||
else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
||||
}
|
||||
|
||||
public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
value &= ~3;
|
||||
|
||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
||||
{
|
||||
int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
|
||||
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector | AggregateType.FP32, false);
|
||||
}
|
||||
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
@@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
|
||||
WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
|
||||
Config.SetOutputUserAttribute(index, perPatch: false);
|
||||
Config.SetOutputUserAttribute(index);
|
||||
passthroughAttributes &= ~(1 << index);
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0;
|
||||
if (targetEnabled)
|
||||
{
|
||||
Config.SetOutputUserAttribute(rtIndex, perPatch: false);
|
||||
Config.SetOutputUserAttribute(rtIndex);
|
||||
regIndexBase += 4;
|
||||
}
|
||||
}
|
||||
|
@@ -50,16 +50,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
public bool NextUsesFixedFuncAttributes { get; private set; }
|
||||
public int UsedInputAttributes { get; private set; }
|
||||
public int UsedOutputAttributes { get; private set; }
|
||||
public int UsedInputAttributesPerPatch { get; private set; }
|
||||
public int UsedOutputAttributesPerPatch { get; private set; }
|
||||
public HashSet<int> UsedInputAttributesPerPatch { get; }
|
||||
public HashSet<int> UsedOutputAttributesPerPatch { get; }
|
||||
public HashSet<int> NextUsedInputAttributesPerPatch { get; private set; }
|
||||
public int PassthroughAttributes { get; private set; }
|
||||
private int _nextUsedInputAttributes;
|
||||
private int _thisUsedInputAttributes;
|
||||
private Dictionary<int, int> _perPatchAttributeLocations;
|
||||
|
||||
public UInt128 NextInputAttributesComponents { get; private set; }
|
||||
public UInt128 ThisInputAttributesComponents { get; private set; }
|
||||
public UInt128 NextInputAttributesPerPatchComponents { get; private set; }
|
||||
public UInt128 ThisInputAttributesPerPatchComponents { get; private set; }
|
||||
|
||||
private int _usedConstantBuffers;
|
||||
private int _usedStorageBuffers;
|
||||
@@ -122,6 +122,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
Stage = ShaderStage.Compute;
|
||||
GpuAccessor = gpuAccessor;
|
||||
Options = options;
|
||||
|
||||
UsedInputAttributesPerPatch = new HashSet<int>();
|
||||
UsedOutputAttributesPerPatch = new HashSet<int>();
|
||||
|
||||
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
|
||||
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
||||
}
|
||||
@@ -244,14 +248,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
UsedOutputAttributes |= 1 << index;
|
||||
}
|
||||
|
||||
public void SetInputUserAttribute(int index, int component, bool perPatch)
|
||||
{
|
||||
if (perPatch)
|
||||
{
|
||||
UsedInputAttributesPerPatch |= 1 << index;
|
||||
ThisInputAttributesPerPatchComponents |= UInt128.Pow2(index * 4 + component);
|
||||
}
|
||||
else
|
||||
public void SetInputUserAttribute(int index, int component)
|
||||
{
|
||||
int mask = 1 << index;
|
||||
|
||||
@@ -259,34 +256,63 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_thisUsedInputAttributes |= mask;
|
||||
ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component);
|
||||
}
|
||||
|
||||
public void SetInputUserAttributePerPatch(int index)
|
||||
{
|
||||
UsedInputAttributesPerPatch.Add(index);
|
||||
}
|
||||
|
||||
public void SetOutputUserAttribute(int index, bool perPatch)
|
||||
{
|
||||
if (perPatch)
|
||||
{
|
||||
UsedOutputAttributesPerPatch |= 1 << index;
|
||||
}
|
||||
else
|
||||
public void SetOutputUserAttribute(int index)
|
||||
{
|
||||
UsedOutputAttributes |= 1 << index;
|
||||
}
|
||||
|
||||
public void SetOutputUserAttributePerPatch(int index)
|
||||
{
|
||||
UsedOutputAttributesPerPatch.Add(index);
|
||||
}
|
||||
|
||||
public void MergeFromtNextStage(ShaderConfig config)
|
||||
{
|
||||
NextInputAttributesComponents = config.ThisInputAttributesComponents;
|
||||
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
|
||||
NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch;
|
||||
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
|
||||
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
|
||||
|
||||
if (UsedOutputAttributesPerPatch.Count != 0)
|
||||
{
|
||||
// Regular and per-patch input/output locations can't overlap,
|
||||
// so we must assign on our location using unused regular input/output locations.
|
||||
|
||||
Dictionary<int, int> locationsMap = new Dictionary<int, int>();
|
||||
|
||||
int freeMask = ~UsedOutputAttributes;
|
||||
|
||||
foreach (int attr in UsedOutputAttributesPerPatch)
|
||||
{
|
||||
int location = BitOperations.TrailingZeroCount(freeMask);
|
||||
if (location == 32)
|
||||
{
|
||||
config.GpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}.");
|
||||
break;
|
||||
}
|
||||
|
||||
locationsMap.Add(attr, location);
|
||||
freeMask &= ~(1 << location);
|
||||
}
|
||||
|
||||
// Both stages must agree on the locations, so use the same "map" for both.
|
||||
_perPatchAttributeLocations = locationsMap;
|
||||
config._perPatchAttributeLocations = locationsMap;
|
||||
}
|
||||
|
||||
if (config.Stage != ShaderStage.Fragment)
|
||||
{
|
||||
LastInVertexPipeline = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
|
||||
public void MergeOutputUserAttributes(int mask, IEnumerable<int> perPatch)
|
||||
{
|
||||
_nextUsedInputAttributes = mask;
|
||||
|
||||
@@ -297,10 +323,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
else
|
||||
{
|
||||
UsedOutputAttributes |= mask;
|
||||
UsedOutputAttributesPerPatch |= maskPerPatch;
|
||||
UsedOutputAttributesPerPatch.UnionWith(perPatch);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetPerPatchAttributeLocation(int index)
|
||||
{
|
||||
if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location))
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
public bool IsUsedOutputAttribute(int attr)
|
||||
{
|
||||
// The check for fixed function attributes on the next stage is conservative,
|
||||
|
@@ -204,14 +204,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
|
||||
}
|
||||
|
||||
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents;
|
||||
while (usedAttributesPerPatch != UInt128.Zero)
|
||||
if (context.Config.NextUsedInputAttributesPerPatch != null)
|
||||
{
|
||||
int index = usedAttributesPerPatch.TrailingZeroCount();
|
||||
|
||||
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: true);
|
||||
|
||||
usedAttributesPerPatch &= ~UInt128.Pow2(index);
|
||||
foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.OrderBy(x => x))
|
||||
{
|
||||
InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.NextUsesFixedFuncAttributes)
|
||||
@@ -236,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int attrOffset = baseAttr + c * 4;
|
||||
context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f));
|
||||
InitializeOutputComponent(context, attrOffset, perPatch);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
using static Ryujinx.Graphics.Shader.Translation.Translator;
|
||||
@@ -137,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
if (other != null)
|
||||
{
|
||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, 0);
|
||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
|
||||
|
||||
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
|
||||
|
||||
|
@@ -370,6 +370,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
|
||||
|
||||
_gd.PipelineInternal.EndRenderPass();
|
||||
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
|
||||
|
||||
_cachedConvertedBuffers.Add(offset, size, key, holder);
|
||||
@@ -388,6 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
|
||||
|
||||
_gd.PipelineInternal.EndRenderPass();
|
||||
_gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
|
||||
|
||||
key.SetBuffer(holder.GetBuffer());
|
||||
@@ -398,6 +400,29 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return holder.GetBuffer();
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
||||
{
|
||||
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
|
||||
|
||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||
{
|
||||
// The destination index size is always I32.
|
||||
|
||||
int indexCount = size / indexSize;
|
||||
|
||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
||||
|
||||
holder = _gd.BufferManager.Create(_gd, convertedCount * 4);
|
||||
|
||||
_gd.PipelineInternal.EndRenderPass();
|
||||
_gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount);
|
||||
|
||||
_cachedConvertedBuffers.Add(offset, size, key, holder);
|
||||
}
|
||||
|
||||
return holder.GetBuffer();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
|
||||
|
@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return null;
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -9,38 +8,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private readonly int _offset;
|
||||
private readonly int _size;
|
||||
private readonly IndexType _type;
|
||||
|
||||
private readonly Auto<DisposableBuffer> _buffer;
|
||||
|
||||
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
|
||||
{
|
||||
_buffer = buffer;
|
||||
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_type = type;
|
||||
buffer?.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
|
||||
{
|
||||
_buffer = buffer;
|
||||
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_type = IndexType.Uint16;
|
||||
buffer?.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
public void BindIndexBuffer(Vk api, CommandBufferScoped cbs)
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
api.CmdBindIndexBuffer(cbs.CommandBuffer, _buffer.Get(cbs, _offset, _size).Value, (ulong)_offset, _type);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
|
||||
{
|
||||
if (_buffer != null)
|
||||
|
@@ -10,14 +10,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
struct I8ToI16CacheKey : ICacheKey
|
||||
{
|
||||
public I8ToI16CacheKey() { }
|
||||
// Used to notify the pipeline that bindings have invalidated on dispose.
|
||||
private readonly VulkanRenderer _gd;
|
||||
private Auto<DisposableBuffer> _buffer;
|
||||
|
||||
public I8ToI16CacheKey(VulkanRenderer gd)
|
||||
{
|
||||
_gd = gd;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
public bool KeyEqual(ICacheKey other)
|
||||
{
|
||||
return other is I8ToI16CacheKey;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
public void Dispose()
|
||||
{
|
||||
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
struct AlignedVertexBufferCacheKey : ICacheKey
|
||||
@@ -55,6 +66,41 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
struct TopologyConversionCacheKey : ICacheKey
|
||||
{
|
||||
private IndexBufferPattern _pattern;
|
||||
private int _indexSize;
|
||||
|
||||
// Used to notify the pipeline that bindings have invalidated on dispose.
|
||||
private readonly VulkanRenderer _gd;
|
||||
private Auto<DisposableBuffer> _buffer;
|
||||
|
||||
public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize)
|
||||
{
|
||||
_gd = gd;
|
||||
_pattern = pattern;
|
||||
_indexSize = indexSize;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
public bool KeyEqual(ICacheKey other)
|
||||
{
|
||||
return other is TopologyConversionCacheKey entry &&
|
||||
entry._pattern == _pattern &&
|
||||
entry._indexSize == _indexSize;
|
||||
}
|
||||
|
||||
public void SetBuffer(Auto<DisposableBuffer> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
struct CacheByRange<T> where T : IDisposable
|
||||
{
|
||||
private struct Entry
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -179,8 +180,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
|
||||
GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
|
||||
GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
|
||||
GAL.PrimitiveTopology.Quads => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, // Emulated with triangle fans
|
||||
GAL.PrimitiveTopology.QuadStrip => Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip, // Emulated with triangle strips
|
||||
GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
|
||||
GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
|
||||
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
|
||||
};
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
|
||||
using Ryujinx.Graphics.Vulkan.Shaders;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
@@ -399,6 +400,86 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
newSize);
|
||||
}
|
||||
|
||||
public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
|
||||
CommandBufferScoped cbs,
|
||||
BufferHolder src,
|
||||
BufferHolder dst,
|
||||
IndexBufferPattern pattern,
|
||||
int indexSize,
|
||||
int srcOffset,
|
||||
int indexCount)
|
||||
{
|
||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
||||
int outputIndexSize = 4;
|
||||
|
||||
// TODO: Do this with a compute shader?
|
||||
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
|
||||
var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
|
||||
|
||||
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
|
||||
|
||||
var bufferCopy = new List<BufferCopy>();
|
||||
int outputOffset = 0;
|
||||
|
||||
// Try to merge copies of adjacent indices to reduce copy count.
|
||||
int sequenceStart = 0;
|
||||
int sequenceLength = 0;
|
||||
|
||||
foreach (var index in pattern.GetIndexMapping(indexCount))
|
||||
{
|
||||
if (sequenceLength > 0)
|
||||
{
|
||||
if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
|
||||
{
|
||||
sequenceLength++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Commit the copy so far.
|
||||
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
|
||||
outputOffset += outputIndexSize * sequenceLength;
|
||||
}
|
||||
|
||||
sequenceStart = index;
|
||||
sequenceLength = 1;
|
||||
}
|
||||
|
||||
if (sequenceLength > 0)
|
||||
{
|
||||
// Commit final pending copy.
|
||||
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
|
||||
}
|
||||
|
||||
var bufferCopyArray = bufferCopy.ToArray();
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
0,
|
||||
convertedCount * outputIndexSize);
|
||||
|
||||
fixed (BufferCopy* pBufferCopy = bufferCopyArray)
|
||||
{
|
||||
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
|
||||
}
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
0,
|
||||
convertedCount * outputIndexSize);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
139
Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
Normal file
139
Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
internal class IndexBufferPattern : IDisposable
|
||||
{
|
||||
public int PrimitiveVertices { get; }
|
||||
public int PrimitiveVerticesOut { get; }
|
||||
public int BaseIndex { get; }
|
||||
public int[] OffsetIndex { get; }
|
||||
public int IndexStride { get; }
|
||||
public bool RepeatStart { get; }
|
||||
|
||||
private VulkanRenderer _gd;
|
||||
private int _currentSize;
|
||||
private BufferHandle _repeatingBuffer;
|
||||
|
||||
public IndexBufferPattern(VulkanRenderer gd,
|
||||
int primitiveVertices,
|
||||
int primitiveVerticesOut,
|
||||
int baseIndex,
|
||||
int[] offsetIndex,
|
||||
int indexStride,
|
||||
bool repeatStart)
|
||||
{
|
||||
PrimitiveVertices = primitiveVertices;
|
||||
PrimitiveVerticesOut = primitiveVerticesOut;
|
||||
BaseIndex = baseIndex;
|
||||
OffsetIndex = offsetIndex;
|
||||
IndexStride = indexStride;
|
||||
RepeatStart = repeatStart;
|
||||
|
||||
_gd = gd;
|
||||
}
|
||||
|
||||
public int GetPrimitiveCount(int vertexCount)
|
||||
{
|
||||
return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
|
||||
}
|
||||
|
||||
public int GetConvertedCount(int indexCount)
|
||||
{
|
||||
int primitiveCount = GetPrimitiveCount(indexCount);
|
||||
return primitiveCount * OffsetIndex.Length;
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetIndexMapping(int indexCount)
|
||||
{
|
||||
int primitiveCount = GetPrimitiveCount(indexCount);
|
||||
int index = BaseIndex;
|
||||
|
||||
for (int i = 0; i < primitiveCount; i++)
|
||||
{
|
||||
if (RepeatStart)
|
||||
{
|
||||
// Used for triangle fan
|
||||
yield return 0;
|
||||
}
|
||||
|
||||
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
|
||||
{
|
||||
yield return index + OffsetIndex[j];
|
||||
}
|
||||
|
||||
index += IndexStride;
|
||||
}
|
||||
}
|
||||
|
||||
public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
|
||||
{
|
||||
int primitiveCount = GetPrimitiveCount(vertexCount);
|
||||
indexCount = primitiveCount * PrimitiveVerticesOut;
|
||||
|
||||
int expectedSize = primitiveCount * OffsetIndex.Length;
|
||||
|
||||
if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
|
||||
{
|
||||
return _repeatingBuffer;
|
||||
}
|
||||
|
||||
// Expand the repeating pattern to the number of requested primitives.
|
||||
BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
|
||||
|
||||
// Copy the old data to the new one.
|
||||
if (_repeatingBuffer != BufferHandle.Null)
|
||||
{
|
||||
_gd.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
|
||||
_gd.DeleteBuffer(_repeatingBuffer);
|
||||
}
|
||||
|
||||
_repeatingBuffer = newBuffer;
|
||||
|
||||
// Add the additional repeats on top.
|
||||
int newPrimitives = primitiveCount;
|
||||
int oldPrimitives = (_currentSize) / OffsetIndex.Length;
|
||||
|
||||
int[] newData;
|
||||
|
||||
newPrimitives -= oldPrimitives;
|
||||
newData = new int[expectedSize - _currentSize];
|
||||
|
||||
int outOffset = 0;
|
||||
int index = oldPrimitives * IndexStride + BaseIndex;
|
||||
|
||||
for (int i = 0; i < newPrimitives; i++)
|
||||
{
|
||||
if (RepeatStart)
|
||||
{
|
||||
// Used for triangle fan
|
||||
newData[outOffset++] = 0;
|
||||
}
|
||||
|
||||
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
|
||||
{
|
||||
newData[outOffset++] = index + OffsetIndex[j];
|
||||
}
|
||||
|
||||
index += IndexStride;
|
||||
}
|
||||
|
||||
_gd.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
|
||||
_currentSize = expectedSize;
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_repeatingBuffer != BufferHandle.Null)
|
||||
{
|
||||
_gd.DeleteBuffer(_repeatingBuffer);
|
||||
_repeatingBuffer = BufferHandle.Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
Ryujinx.Graphics.Vulkan/IndexBufferState.cs
Normal file
97
Ryujinx.Graphics.Vulkan/IndexBufferState.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
internal struct IndexBufferState
|
||||
{
|
||||
public static IndexBufferState Null => new IndexBufferState(GAL.BufferHandle.Null, 0, 0);
|
||||
|
||||
private readonly int _offset;
|
||||
private readonly int _size;
|
||||
private readonly IndexType _type;
|
||||
|
||||
private readonly GAL.BufferHandle _handle;
|
||||
private Auto<DisposableBuffer> _buffer;
|
||||
|
||||
public IndexBufferState(GAL.BufferHandle handle, int offset, int size, IndexType type)
|
||||
{
|
||||
_handle = handle;
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_type = type;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
public IndexBufferState(GAL.BufferHandle handle, int offset, int size)
|
||||
{
|
||||
_handle = handle;
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_type = IndexType.Uint16;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
public void BindIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs)
|
||||
{
|
||||
Auto<DisposableBuffer> autoBuffer;
|
||||
int offset, size;
|
||||
IndexType type = _type;
|
||||
|
||||
if (_type == IndexType.Uint8Ext && !gd.Capabilities.SupportsIndexTypeUint8)
|
||||
{
|
||||
// Index type is not supported. Convert to I16.
|
||||
autoBuffer = gd.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size);
|
||||
|
||||
type = IndexType.Uint16;
|
||||
offset = 0;
|
||||
size = _size * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
|
||||
|
||||
offset = _offset;
|
||||
size = _size;
|
||||
}
|
||||
|
||||
_buffer = autoBuffer;
|
||||
|
||||
if (autoBuffer != null)
|
||||
{
|
||||
gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, offset, size).Value, (ulong)offset, type);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern)
|
||||
{
|
||||
Auto<DisposableBuffer> autoBuffer;
|
||||
|
||||
// Convert the index buffer using the given pattern.
|
||||
int indexSize = _type switch
|
||||
{
|
||||
IndexType.Uint32 => 4,
|
||||
IndexType.Uint16 => 2,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
int firstIndexOffset = firstIndex * indexSize;
|
||||
|
||||
autoBuffer = gd.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize);
|
||||
|
||||
int size = convertedCount * 4;
|
||||
|
||||
_buffer = autoBuffer;
|
||||
|
||||
if (autoBuffer != null)
|
||||
{
|
||||
gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, 0, size).Value, 0, IndexType.Uint32);
|
||||
}
|
||||
}
|
||||
|
||||
public bool BoundEquals(Auto<DisposableBuffer> buffer)
|
||||
{
|
||||
return _buffer == buffer;
|
||||
}
|
||||
}
|
||||
}
|
@@ -51,13 +51,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private readonly DescriptorSetUpdater _descriptorSetUpdater;
|
||||
|
||||
private BufferState _indexBuffer;
|
||||
private IndexBufferState _indexBuffer;
|
||||
private IndexBufferPattern _indexBufferPattern;
|
||||
private readonly BufferState[] _transformFeedbackBuffers;
|
||||
private readonly VertexBufferState[] _vertexBuffers;
|
||||
private ulong _vertexBuffersDirty;
|
||||
protected Rectangle<int> ClearScissor;
|
||||
|
||||
public SupportBufferUpdater SupportBufferUpdater;
|
||||
public IndexBufferPattern QuadsToTrisPattern;
|
||||
public IndexBufferPattern TriFanToTrisPattern;
|
||||
|
||||
private bool _needsIndexBufferRebind;
|
||||
private bool _needsTransformFeedbackBuffersRebind;
|
||||
@@ -65,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private bool _tfEnabled;
|
||||
private bool _tfActive;
|
||||
|
||||
private PipelineColorBlendAttachmentState[] _storedBlend;
|
||||
|
||||
public ulong DrawCount { get; private set; }
|
||||
|
||||
public unsafe PipelineBase(VulkanRenderer gd, Device device)
|
||||
@@ -101,12 +106,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_newState.Initialize();
|
||||
_newState.LineWidth = 1f;
|
||||
_newState.SamplesCount = 1;
|
||||
|
||||
_storedBlend = new PipelineColorBlendAttachmentState[8];
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
SupportBufferUpdater = new SupportBufferUpdater(Gd);
|
||||
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
|
||||
|
||||
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
|
||||
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
|
||||
}
|
||||
|
||||
public unsafe void Barrier()
|
||||
@@ -245,6 +255,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public void DirtyIndexBuffer(Auto<DisposableBuffer> buffer)
|
||||
{
|
||||
if (_indexBuffer.BoundEquals(buffer))
|
||||
{
|
||||
_needsIndexBufferRebind = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
@@ -267,24 +285,59 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
|
||||
BeginRenderPass();
|
||||
ResumeTransformFeedbackInternal();
|
||||
DrawCount++;
|
||||
|
||||
if (_topology == GAL.PrimitiveTopology.Quads)
|
||||
if (Gd.TopologyUnsupported(_topology))
|
||||
{
|
||||
int quadsCount = vertexCount / 4;
|
||||
// Temporarily bind a conversion pattern as an index buffer.
|
||||
_needsIndexBufferRebind = true;
|
||||
|
||||
for (int i = 0; i < quadsCount; i++)
|
||||
IndexBufferPattern pattern = _topology switch
|
||||
{
|
||||
Gd.Api.CmdDraw(CommandBuffer, 4, (uint)instanceCount, (uint)(firstVertex + i * 4), (uint)firstInstance);
|
||||
}
|
||||
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
|
||||
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
|
||||
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
|
||||
};
|
||||
|
||||
BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
|
||||
var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, handle, false);
|
||||
|
||||
Gd.Api.CmdBindIndexBuffer(CommandBuffer, buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, 0, Silk.NET.Vulkan.IndexType.Uint32);
|
||||
|
||||
BeginRenderPass(); // May have been interrupted to set buffer data.
|
||||
ResumeTransformFeedbackInternal();
|
||||
|
||||
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResumeTransformFeedbackInternal();
|
||||
|
||||
Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIndexBufferPattern()
|
||||
{
|
||||
IndexBufferPattern pattern = null;
|
||||
|
||||
if (Gd.TopologyUnsupported(_topology))
|
||||
{
|
||||
pattern = _topology switch
|
||||
{
|
||||
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
|
||||
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
|
||||
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
|
||||
};
|
||||
}
|
||||
|
||||
if (_indexBufferPattern != pattern)
|
||||
{
|
||||
_indexBufferPattern = pattern;
|
||||
_needsIndexBufferRebind = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
@@ -292,22 +345,34 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateIndexBufferPattern();
|
||||
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
|
||||
BeginRenderPass();
|
||||
ResumeTransformFeedbackInternal();
|
||||
DrawCount++;
|
||||
|
||||
if (_topology == GAL.PrimitiveTopology.Quads)
|
||||
if (_indexBufferPattern != null)
|
||||
{
|
||||
int quadsCount = indexCount / 4;
|
||||
// Convert the index buffer into a supported topology.
|
||||
IndexBufferPattern pattern = _indexBufferPattern;
|
||||
|
||||
for (int i = 0; i < quadsCount; i++)
|
||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
||||
|
||||
if (_needsIndexBufferRebind)
|
||||
{
|
||||
Gd.Api.CmdDrawIndexed(CommandBuffer, 4, (uint)instanceCount, (uint)(firstIndex + i * 4), firstVertex, (uint)firstInstance);
|
||||
_indexBuffer.BindConvertedIndexBuffer(Gd, Cbs, firstIndex, indexCount, convertedCount, pattern);
|
||||
|
||||
_needsIndexBufferRebind = false;
|
||||
}
|
||||
|
||||
BeginRenderPass(); // May have been interrupted to set buffer data.
|
||||
ResumeTransformFeedbackInternal();
|
||||
|
||||
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)convertedCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResumeTransformFeedbackInternal();
|
||||
|
||||
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance);
|
||||
}
|
||||
}
|
||||
@@ -437,6 +502,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index];
|
||||
|
||||
if (blend.Enable)
|
||||
{
|
||||
vkBlend.BlendEnable = blend.Enable;
|
||||
vkBlend.SrcColorBlendFactor = blend.ColorSrcFactor.Convert();
|
||||
vkBlend.DstColorBlendFactor = blend.ColorDstFactor.Convert();
|
||||
@@ -444,6 +511,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
vkBlend.SrcAlphaBlendFactor = blend.AlphaSrcFactor.Convert();
|
||||
vkBlend.DstAlphaBlendFactor = blend.AlphaDstFactor.Convert();
|
||||
vkBlend.AlphaBlendOp = blend.AlphaOp.Convert();
|
||||
}
|
||||
else
|
||||
{
|
||||
vkBlend = new PipelineColorBlendAttachmentState(
|
||||
colorWriteMask: vkBlend.ColorWriteMask);
|
||||
}
|
||||
|
||||
if (vkBlend.ColorWriteMask == 0)
|
||||
{
|
||||
_storedBlend[index] = vkBlend;
|
||||
|
||||
vkBlend = new PipelineColorBlendAttachmentState();
|
||||
}
|
||||
|
||||
_newState.BlendConstantR = blend.BlendConstant.Red;
|
||||
_newState.BlendConstantG = blend.BlendConstant.Green;
|
||||
@@ -500,34 +580,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
|
||||
{
|
||||
_indexBuffer.Dispose();
|
||||
|
||||
if (buffer.Handle != BufferHandle.Null)
|
||||
{
|
||||
Auto<DisposableBuffer> ib = null;
|
||||
int offset = buffer.Offset;
|
||||
int size = buffer.Size;
|
||||
|
||||
if (type == GAL.IndexType.UByte && !Gd.Capabilities.SupportsIndexTypeUint8)
|
||||
{
|
||||
ib = Gd.BufferManager.GetBufferI8ToI16(Cbs, buffer.Handle, offset, size);
|
||||
offset = 0;
|
||||
size *= 2;
|
||||
type = GAL.IndexType.UShort;
|
||||
_indexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type.Convert());
|
||||
}
|
||||
else
|
||||
{
|
||||
ib = Gd.BufferManager.GetBuffer(CommandBuffer, buffer.Handle, false);
|
||||
_indexBuffer = IndexBufferState.Null;
|
||||
}
|
||||
|
||||
_indexBuffer = new BufferState(ib, offset, size, type.Convert());
|
||||
}
|
||||
else
|
||||
{
|
||||
_indexBuffer = BufferState.Null;
|
||||
}
|
||||
|
||||
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
|
||||
_needsIndexBufferRebind = true;
|
||||
}
|
||||
|
||||
public void SetLineParameters(float width, bool smooth)
|
||||
@@ -584,7 +646,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_topology = topology;
|
||||
|
||||
var vkTopology = topology.Convert();
|
||||
var vkTopology = Gd.TopologyRemap(topology).Convert();
|
||||
|
||||
_newState.Topology = vkTopology;
|
||||
|
||||
@@ -626,8 +688,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i];
|
||||
var newMask = (ColorComponentFlags)componentMask[i];
|
||||
|
||||
vkBlend.ColorWriteMask = (ColorComponentFlags)componentMask[i];
|
||||
// When color write mask is 0, remove all blend state to help the pipeline cache.
|
||||
// Restore it when the mask becomes non-zero.
|
||||
if (vkBlend.ColorWriteMask != newMask)
|
||||
{
|
||||
if (newMask == 0)
|
||||
{
|
||||
_storedBlend[i] = vkBlend;
|
||||
|
||||
vkBlend = new PipelineColorBlendAttachmentState();
|
||||
}
|
||||
else if (vkBlend.ColorWriteMask == 0)
|
||||
{
|
||||
vkBlend = _storedBlend[i];
|
||||
}
|
||||
}
|
||||
|
||||
vkBlend.ColorWriteMask = newMask;
|
||||
|
||||
if (componentMask[i] != 0)
|
||||
{
|
||||
@@ -1127,9 +1206,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
// Commit changes to the support buffer before drawing.
|
||||
SupportBufferUpdater.Commit();
|
||||
|
||||
if (_needsIndexBufferRebind)
|
||||
if (_needsIndexBufferRebind && _indexBufferPattern == null)
|
||||
{
|
||||
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
|
||||
_indexBuffer.BindIndexBuffer(Gd, Cbs);
|
||||
_needsIndexBufferRebind = false;
|
||||
}
|
||||
|
||||
@@ -1265,7 +1344,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_renderPass?.Dispose();
|
||||
_framebuffer?.Dispose();
|
||||
_indexBuffer.Dispose();
|
||||
_newState.Dispose();
|
||||
_descriptorSetUpdater.Dispose();
|
||||
|
||||
|
@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
|
||||
|
||||
pipeline.Topology = state.Topology.Convert();
|
||||
pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
|
||||
|
||||
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
|
||||
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
|
||||
@@ -257,6 +257,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
var blend = state.BlendDescriptors[i];
|
||||
|
||||
if (blend.Enable && state.ColorWriteMask[i] != 0)
|
||||
{
|
||||
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
|
||||
blend.Enable,
|
||||
blend.ColorSrcFactor.Convert(),
|
||||
@@ -267,6 +269,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
blend.AlphaOp.Convert(),
|
||||
(ColorComponentFlags)state.ColorWriteMask[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
|
||||
colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int maxAttachmentIndex = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
|
@@ -471,6 +471,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||
}
|
||||
|
||||
public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
|
||||
GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
|
||||
_ => topology
|
||||
};
|
||||
}
|
||||
|
||||
public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
GAL.PrimitiveTopology.Quads => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public void Initialize(GraphicsDebugLevel logLevel)
|
||||
{
|
||||
SetupContext(logLevel);
|
||||
|
@@ -22,7 +22,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
// outside of the AccountManager.
|
||||
private readonly HorizonClient _horizonClient;
|
||||
|
||||
private ConcurrentDictionary<string, UserProfile> _profiles;
|
||||
private readonly ConcurrentDictionary<string, UserProfile> _profiles;
|
||||
private UserProfile[] _storedOpenedUsers;
|
||||
|
||||
public UserProfile LastOpenedUser { get; private set; }
|
||||
|
||||
@@ -31,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
_horizonClient = horizonClient;
|
||||
|
||||
_profiles = new ConcurrentDictionary<string, UserProfile>();
|
||||
_storedOpenedUsers = Array.Empty<UserProfile>();
|
||||
|
||||
_accountSaveDataManager = new AccountSaveDataManager(_profiles);
|
||||
|
||||
@@ -221,6 +223,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
return _profiles.Values.Where(x => x.AccountState == AccountState.Open);
|
||||
}
|
||||
|
||||
internal IEnumerable<UserProfile> GetStoredOpenedUsers()
|
||||
{
|
||||
return _storedOpenedUsers;
|
||||
}
|
||||
|
||||
internal void StoreOpenedUsers()
|
||||
{
|
||||
_storedOpenedUsers = _profiles.Values.Where(x => x.AccountState == AccountState.Open).ToArray();
|
||||
}
|
||||
|
||||
internal UserProfile GetFirst()
|
||||
{
|
||||
return _profiles.First().Value;
|
||||
|
@@ -162,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
|
||||
public ResultCode StoreOpenContext(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
context.Device.System.AccountManager.StoreOpenedUsers();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@@ -201,6 +201,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode ListOpenContextStoredUsers(ServiceCtx context)
|
||||
{
|
||||
return WriteUserList(context, context.Device.System.AccountManager.GetStoredOpenedUsers());
|
||||
}
|
||||
|
||||
public ResultCode ListQualifiedUsers(ServiceCtx context)
|
||||
{
|
||||
// TODO: Determine how users are "qualified". We assume all users are "qualified" for now.
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService;
|
||||
using Ryujinx.HLE.HOS.Services.Arp;
|
||||
|
||||
@@ -139,20 +138,21 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
return _applicationServiceServer.ClearSaveDataThumbnail(context);
|
||||
}
|
||||
|
||||
[CommandHipc(130)] // 5.0.0+
|
||||
// LoadOpenContext(nn::account::Uid)
|
||||
public ResultCode LoadOpenContext(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(60)] // 5.0.0-5.1.0
|
||||
[CommandHipc(131)] // 6.0.0+
|
||||
// ListOpenContextStoredUsers() -> array<nn::account::Uid, 0xa>
|
||||
public ResultCode ListOpenContextStoredUsers(ServiceCtx context)
|
||||
{
|
||||
ulong outputPosition = context.Request.RecvListBuff[0].Position;
|
||||
ulong outputSize = context.Request.RecvListBuff[0].Size;
|
||||
|
||||
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
|
||||
|
||||
// TODO: This seems to write stored userids of the OpenContext in the buffer. We needs to determine them.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
|
||||
return ResultCode.Success;
|
||||
return _applicationServiceServer.ListOpenContextStoredUsers(context);
|
||||
}
|
||||
|
||||
[CommandHipc(141)] // 6.0.0+
|
||||
|
@@ -181,7 +181,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetCertificates(ReadOnlySpan<CaCertificateId> ids, out CertStoreEntry[] entries)
|
||||
public bool TryGetCertificates(
|
||||
ReadOnlySpan<CaCertificateId> ids,
|
||||
out CertStoreEntry[] entries,
|
||||
out bool hasAllCertificates,
|
||||
out int requiredSize)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -190,7 +194,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
throw new InvalidSystemResourceException(CertStoreTitleMissingErrorMessage);
|
||||
}
|
||||
|
||||
bool hasAllCertificates = false;
|
||||
requiredSize = 0;
|
||||
hasAllCertificates = false;
|
||||
|
||||
foreach (CaCertificateId id in ids)
|
||||
{
|
||||
@@ -205,12 +210,14 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
if (hasAllCertificates)
|
||||
{
|
||||
entries = new CertStoreEntry[_certificates.Count];
|
||||
requiredSize = (_certificates.Count + 1) * Unsafe.SizeOf<BuiltInCertificateInfo>();
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (CertStoreEntry entry in _certificates.Values)
|
||||
{
|
||||
entries[i++] = entry;
|
||||
requiredSize += (entry.Data.Length + 3) & ~3;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -218,6 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
else
|
||||
{
|
||||
entries = new CertStoreEntry[ids.Length];
|
||||
requiredSize = ids.Length * Unsafe.SizeOf<BuiltInCertificateInfo>();
|
||||
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
{
|
||||
@@ -227,6 +235,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
}
|
||||
|
||||
entries[i] = entry;
|
||||
requiredSize += (entry.Data.Length + 3) & ~3;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -29,42 +29,40 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private uint ComputeCertificateBufferSizeRequired(ReadOnlySpan<BuiltInCertificateManager.CertStoreEntry> entries)
|
||||
{
|
||||
uint totalSize = 0;
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
totalSize += (uint)Unsafe.SizeOf<BuiltInCertificateInfo>();
|
||||
totalSize += (uint)entries[i].Data.Length;
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
[CommandHipc(2)]
|
||||
// GetCertificates(buffer<CaCertificateId, 5> ids) -> (u32 certificates_count, buffer<bytes, 6> certificates)
|
||||
public ResultCode GetCertificates(ServiceCtx context)
|
||||
{
|
||||
ReadOnlySpan<CaCertificateId> ids = MemoryMarshal.Cast<byte, CaCertificateId>(context.Memory.GetSpan(context.Request.SendBuff[0].Position, (int)context.Request.SendBuff[0].Size));
|
||||
|
||||
if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out BuiltInCertificateManager.CertStoreEntry[] entries))
|
||||
if (!BuiltInCertificateManager.Instance.TryGetCertificates(
|
||||
ids,
|
||||
out BuiltInCertificateManager.CertStoreEntry[] entries,
|
||||
out bool hasAllCertificates,
|
||||
out int requiredSize))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (ComputeCertificateBufferSizeRequired(entries) > context.Request.ReceiveBuff[0].Size)
|
||||
if ((uint)requiredSize > (uint)context.Request.ReceiveBuff[0].Size)
|
||||
{
|
||||
return ResultCode.InvalidCertBufSize;
|
||||
}
|
||||
|
||||
int infosCount = entries.Length;
|
||||
|
||||
if (hasAllCertificates)
|
||||
{
|
||||
infosCount++;
|
||||
}
|
||||
|
||||
using (WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size))
|
||||
{
|
||||
Span<byte> rawData = region.Memory.Span;
|
||||
Span<BuiltInCertificateInfo> infos = MemoryMarshal.Cast<byte, BuiltInCertificateInfo>(rawData)[..entries.Length];
|
||||
Span<byte> certificatesData = rawData[(Unsafe.SizeOf<BuiltInCertificateInfo>() * entries.Length)..];
|
||||
Span<BuiltInCertificateInfo> infos = MemoryMarshal.Cast<byte, BuiltInCertificateInfo>(rawData)[..infosCount];
|
||||
Span<byte> certificatesData = rawData[(Unsafe.SizeOf<BuiltInCertificateInfo>() * infosCount)..];
|
||||
|
||||
for (int i = 0; i < infos.Length; i++)
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
entries[i].Data.CopyTo(certificatesData);
|
||||
|
||||
@@ -78,6 +76,17 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
|
||||
certificatesData = certificatesData[entries[i].Data.Length..];
|
||||
}
|
||||
|
||||
if (hasAllCertificates)
|
||||
{
|
||||
infos[entries.Length] = new BuiltInCertificateInfo
|
||||
{
|
||||
Id = CaCertificateId.All,
|
||||
Status = TrustedCertStatus.Invalid,
|
||||
CertificateDataSize = 0,
|
||||
CertificateDataOffset = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
context.ResponseData.Write(entries.Length);
|
||||
@@ -91,12 +100,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
{
|
||||
ReadOnlySpan<CaCertificateId> ids = MemoryMarshal.Cast<byte, CaCertificateId>(context.Memory.GetSpan(context.Request.SendBuff[0].Position, (int)context.Request.SendBuff[0].Size));
|
||||
|
||||
if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out BuiltInCertificateManager.CertStoreEntry[] entries))
|
||||
if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out _, out _, out int requiredSize))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
context.ResponseData.Write(ComputeCertificateBufferSizeRequired(entries));
|
||||
context.ResponseData.Write(requiredSize);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@@ -162,7 +162,7 @@ namespace Ryujinx.Tests.Cpu
|
||||
_context.SetPstateFlag(PState.ZFlag, zero);
|
||||
_context.SetPstateFlag(PState.NFlag, negative);
|
||||
|
||||
SetFpscr((uint)fpscr);
|
||||
_context.Fpscr = (FPSCR)fpscr;
|
||||
|
||||
_context.SetPstateFlag(PState.TFlag, thumb);
|
||||
|
||||
@@ -467,7 +467,7 @@ namespace Ryujinx.Tests.Cpu
|
||||
Assert.That(_context.GetPstateFlag(PState.NFlag), Is.EqualTo(_unicornEmu.NegativeFlag), "NFlag");
|
||||
});
|
||||
|
||||
Assert.That((int)GetFpscr() & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpscr & (int)fpsrMask), "Fpscr");
|
||||
Assert.That((int)_context.Fpscr & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpscr & (int)fpsrMask), "Fpscr");
|
||||
|
||||
if (_usingMemory)
|
||||
{
|
||||
@@ -650,28 +650,5 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
return rnd & 0x800FFFFFFFFFFFFFul;
|
||||
}
|
||||
|
||||
private uint GetFpscr()
|
||||
{
|
||||
uint fpscr = (uint)(_context.Fpsr & FPSR.A32Mask & ~FPSR.Nzcv) | (uint)(_context.Fpcr & FPCR.A32Mask);
|
||||
|
||||
fpscr |= _context.GetFPstateFlag(FPState.NFlag) ? (1u << (int)FPState.NFlag) : 0;
|
||||
fpscr |= _context.GetFPstateFlag(FPState.ZFlag) ? (1u << (int)FPState.ZFlag) : 0;
|
||||
fpscr |= _context.GetFPstateFlag(FPState.CFlag) ? (1u << (int)FPState.CFlag) : 0;
|
||||
fpscr |= _context.GetFPstateFlag(FPState.VFlag) ? (1u << (int)FPState.VFlag) : 0;
|
||||
|
||||
return fpscr;
|
||||
}
|
||||
|
||||
private void SetFpscr(uint fpscr)
|
||||
{
|
||||
_context.Fpsr = FPSR.A32Mask & (FPSR)fpscr;
|
||||
_context.Fpcr = FPCR.A32Mask & (FPCR)fpscr;
|
||||
|
||||
_context.SetFPstateFlag(FPState.NFlag, (fpscr & (1u << (int)FPState.NFlag)) != 0);
|
||||
_context.SetFPstateFlag(FPState.ZFlag, (fpscr & (1u << (int)FPState.ZFlag)) != 0);
|
||||
_context.SetFPstateFlag(FPState.CFlag, (fpscr & (1u << (int)FPState.CFlag)) != 0);
|
||||
_context.SetFPstateFlag(FPState.VFlag, (fpscr & (1u << (int)FPState.VFlag)) != 0);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user