Implement HSET2 shader instruction and fix errors uncovered by Rodrigo tests
This commit is contained in:
parent
65428f5842
commit
b8528c6317
6 changed files with 82 additions and 31 deletions
|
@ -89,6 +89,9 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10));
|
Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10));
|
||||||
Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32));
|
Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32));
|
||||||
Set("0101110100001x", InstEmit.Hmul2, typeof(OpCodeAluReg));
|
Set("0101110100001x", InstEmit.Hmul2, typeof(OpCodeAluReg));
|
||||||
|
Set("0111110x1xxxxx", InstEmit.Hset2, typeof(OpCodeSetCbuf));
|
||||||
|
Set("0111110x0xxxxx", InstEmit.Hset2, typeof(OpCodeHsetImm2x10));
|
||||||
|
Set("0101110100011x", InstEmit.Hset2, typeof(OpCodeSetReg));
|
||||||
Set("0111111x1xxxxx", InstEmit.Hsetp2, typeof(OpCodeSetCbuf));
|
Set("0111111x1xxxxx", InstEmit.Hsetp2, typeof(OpCodeSetCbuf));
|
||||||
Set("0111111x0xxxxx", InstEmit.Hsetp2, typeof(OpCodeHsetImm2x10));
|
Set("0111111x0xxxxx", InstEmit.Hsetp2, typeof(OpCodeHsetImm2x10));
|
||||||
Set("0101110100100x", InstEmit.Hsetp2, typeof(OpCodeSetReg));
|
Set("0101110100100x", InstEmit.Hsetp2, typeof(OpCodeSetReg));
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
static class InstEmitAluHelper
|
static class InstEmitAluHelper
|
||||||
{
|
{
|
||||||
public static int GetIntMin(IntegerType type)
|
public static long GetIntMin(IntegerType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
@ -18,14 +18,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
case IntegerType.S8: return sbyte.MinValue;
|
case IntegerType.S8: return sbyte.MinValue;
|
||||||
case IntegerType.U16: return ushort.MinValue;
|
case IntegerType.U16: return ushort.MinValue;
|
||||||
case IntegerType.S16: return short.MinValue;
|
case IntegerType.S16: return short.MinValue;
|
||||||
case IntegerType.U32: return (int)uint.MinValue;
|
case IntegerType.U32: return uint.MinValue;
|
||||||
case IntegerType.S32: return int.MinValue;
|
case IntegerType.S32: return int.MinValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException($"The type \"{type}\" is not a supported int type.");
|
throw new ArgumentException($"The type \"{type}\" is not a supported int type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetIntMax(IntegerType type)
|
public static long GetIntMax(IntegerType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
case IntegerType.S8: return sbyte.MaxValue;
|
case IntegerType.S8: return sbyte.MaxValue;
|
||||||
case IntegerType.U16: return ushort.MaxValue;
|
case IntegerType.U16: return ushort.MaxValue;
|
||||||
case IntegerType.S16: return short.MaxValue;
|
case IntegerType.S16: return short.MaxValue;
|
||||||
case IntegerType.U32: return unchecked((int)uint.MaxValue);
|
case IntegerType.U32: return uint.MaxValue;
|
||||||
case IntegerType.S32: return int.MaxValue;
|
case IntegerType.S32: return int.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,18 +14,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
OpCodeFArith op = (OpCodeFArith)context.CurrOp;
|
OpCodeFArith op = (OpCodeFArith)context.CurrOp;
|
||||||
|
|
||||||
FPType srcType = (FPType)op.RawOpCode.Extract(8, 2);
|
FPType dstType = (FPType)op.RawOpCode.Extract(8, 2);
|
||||||
FPType dstType = (FPType)op.RawOpCode.Extract(10, 2);
|
FPType srcType = (FPType)op.RawOpCode.Extract(10, 2);
|
||||||
|
|
||||||
bool pass = op.RawOpCode.Extract(40);
|
bool round = op.RawOpCode.Extract(42);
|
||||||
bool negateB = op.RawOpCode.Extract(45);
|
bool negateB = op.RawOpCode.Extract(45);
|
||||||
bool absoluteB = op.RawOpCode.Extract(49);
|
bool absoluteB = op.RawOpCode.Extract(49);
|
||||||
|
|
||||||
pass &= op.RoundingMode == RoundingMode.TowardsNegativeInfinity;
|
|
||||||
|
|
||||||
Operand srcB = context.FPAbsNeg(GetSrcB(context, srcType), absoluteB, negateB);
|
Operand srcB = context.FPAbsNeg(GetSrcB(context, srcType), absoluteB, negateB);
|
||||||
|
|
||||||
if (!pass)
|
if (round)
|
||||||
{
|
{
|
||||||
switch (op.RoundingMode)
|
switch (op.RoundingMode)
|
||||||
{
|
{
|
||||||
|
@ -84,6 +82,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
switch (op.RoundingMode)
|
switch (op.RoundingMode)
|
||||||
{
|
{
|
||||||
|
case RoundingMode.ToNearest:
|
||||||
|
srcB = context.FPRound(srcB);
|
||||||
|
break;
|
||||||
|
|
||||||
case RoundingMode.TowardsNegativeInfinity:
|
case RoundingMode.TowardsNegativeInfinity:
|
||||||
srcB = context.FPFloor(srcB);
|
srcB = context.FPFloor(srcB);
|
||||||
break;
|
break;
|
||||||
|
@ -97,19 +99,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long min = GetIntMin(intType);
|
||||||
|
long max = GetIntMax(intType);
|
||||||
|
|
||||||
|
srcB = context.FPClamp(srcB, ConstF(min), ConstF(max));
|
||||||
|
|
||||||
srcB = context.FPConvertToS32(srcB);
|
srcB = context.FPConvertToS32(srcB);
|
||||||
|
|
||||||
// TODO: S/U64, conversion overflow handling.
|
|
||||||
if (intType != IntegerType.S32)
|
|
||||||
{
|
|
||||||
int min = GetIntMin(intType);
|
|
||||||
int max = GetIntMax(intType);
|
|
||||||
|
|
||||||
srcB = isSignedInt
|
|
||||||
? context.IClampS32(srcB, Const(min), Const(max))
|
|
||||||
: context.IClampU32(srcB, Const(min), Const(max));
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand dest = GetDest(context);
|
Operand dest = GetDest(context);
|
||||||
|
|
||||||
context.Copy(dest, srcB);
|
context.Copy(dest, srcB);
|
||||||
|
@ -194,8 +190,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
dstType |= IntegerType.S8;
|
dstType |= IntegerType.S8;
|
||||||
}
|
}
|
||||||
|
|
||||||
int min = GetIntMin(dstType);
|
int min = (int)GetIntMin(dstType);
|
||||||
int max = GetIntMax(dstType);
|
int max = (int)GetIntMax(dstType);
|
||||||
|
|
||||||
srcB = dstIsSignedInt
|
srcB = dstIsSignedInt
|
||||||
? context.IClampS32(srcB, Const(min), Const(max))
|
? context.IClampS32(srcB, Const(min), Const(max))
|
||||||
|
|
|
@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
bool saturate = op.RawOpCode.Extract(op is IOpCodeReg ? 32 : 52);
|
bool saturate = op.RawOpCode.Extract(op is IOpCodeReg ? 32 : 52);
|
||||||
|
|
||||||
Operand[] srcA = GetHalfSrcA(context);
|
Operand[] srcA = GetHalfSrcA(context, isAdd);
|
||||||
Operand[] srcB = GetHalfSrcB(context);
|
Operand[] srcB = GetHalfSrcB(context);
|
||||||
|
|
||||||
Operand[] res = new Operand[2];
|
Operand[] res = new Operand[2];
|
||||||
|
@ -254,13 +254,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Copy(GetDest(context), GetHalfPacked(context, res));
|
context.Copy(GetDest(context), GetHalfPacked(context, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Hsetp2(EmitterContext context)
|
public static void Hset2(EmitterContext context)
|
||||||
{
|
{
|
||||||
OpCodeSet op = (OpCodeSet)context.CurrOp;
|
OpCodeSet op = (OpCodeSet)context.CurrOp;
|
||||||
|
|
||||||
bool hAnd = op.RawOpCode.Extract(53);
|
bool isRegVariant = op is IOpCodeReg;
|
||||||
|
|
||||||
Condition cmpOp = op is IOpCodeReg
|
bool boolFloat = isRegVariant
|
||||||
|
? op.RawOpCode.Extract(49)
|
||||||
|
: op.RawOpCode.Extract(53);
|
||||||
|
|
||||||
|
Condition cmpOp = isRegVariant
|
||||||
? (Condition)op.RawOpCode.Extract(35, 4)
|
? (Condition)op.RawOpCode.Extract(35, 4)
|
||||||
: (Condition)op.RawOpCode.Extract(49, 4);
|
: (Condition)op.RawOpCode.Extract(49, 4);
|
||||||
|
|
||||||
|
@ -269,8 +273,49 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
Operand[] res = new Operand[2];
|
Operand[] res = new Operand[2];
|
||||||
|
|
||||||
|
res[0] = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
|
||||||
|
res[1] = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
|
||||||
|
|
||||||
Operand pred = GetPredicate39(context);
|
Operand pred = GetPredicate39(context);
|
||||||
|
|
||||||
|
res[0] = GetPredLogicalOp(context, op.LogicalOp, res[0], pred);
|
||||||
|
res[1] = GetPredLogicalOp(context, op.LogicalOp, res[1], pred);
|
||||||
|
|
||||||
|
if (boolFloat)
|
||||||
|
{
|
||||||
|
res[0] = context.ConditionalSelect(res[0], ConstF(1), Const(0));
|
||||||
|
res[1] = context.ConditionalSelect(res[1], ConstF(1), Const(0));
|
||||||
|
|
||||||
|
context.Copy(GetDest(context), context.PackHalf2x16(res[0], res[1]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand low = context.BitwiseAnd(res[0], Const(0xffff));
|
||||||
|
Operand high = context.ShiftLeft (res[1], Const(16));
|
||||||
|
|
||||||
|
Operand packed = context.BitwiseOr(low, high);
|
||||||
|
|
||||||
|
context.Copy(GetDest(context), packed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Hsetp2(EmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSet op = (OpCodeSet)context.CurrOp;
|
||||||
|
|
||||||
|
bool isRegVariant = op is IOpCodeReg;
|
||||||
|
|
||||||
|
bool hAnd = isRegVariant
|
||||||
|
? op.RawOpCode.Extract(49)
|
||||||
|
: op.RawOpCode.Extract(53);
|
||||||
|
|
||||||
|
Condition cmpOp = isRegVariant
|
||||||
|
? (Condition)op.RawOpCode.Extract(35, 4)
|
||||||
|
: (Condition)op.RawOpCode.Extract(49, 4);
|
||||||
|
|
||||||
|
Operand[] srcA = GetHalfSrcA(context);
|
||||||
|
Operand[] srcB = GetHalfSrcB(context);
|
||||||
|
|
||||||
Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
|
Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
|
||||||
Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
|
Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
|
||||||
|
|
||||||
|
@ -280,6 +325,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
p1Res = context.BitwiseNot(p0Res);
|
p1Res = context.BitwiseNot(p0Res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Operand pred = GetPredicate39(context);
|
||||||
|
|
||||||
p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
|
p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
|
||||||
p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
|
p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
// TODO: Warn about invalid floating point type.
|
// TODO: Warn about invalid floating point type.
|
||||||
|
|
||||||
return Const(0);
|
return ConstF(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand GetSrcB(EmitterContext context)
|
public static Operand GetSrcB(EmitterContext context)
|
||||||
|
@ -98,13 +98,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
throw new InvalidOperationException($"Unexpected opcode type \"{context.CurrOp.GetType().Name}\".");
|
throw new InvalidOperationException($"Unexpected opcode type \"{context.CurrOp.GetType().Name}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand[] GetHalfSrcA(EmitterContext context)
|
public static Operand[] GetHalfSrcA(EmitterContext context, bool isAdd = false)
|
||||||
{
|
{
|
||||||
OpCode op = context.CurrOp;
|
OpCode op = context.CurrOp;
|
||||||
|
|
||||||
bool absoluteA = false, negateA = false;
|
bool absoluteA = false, negateA = false;
|
||||||
|
|
||||||
if (op is IOpCodeCbuf || op is IOpCodeImm)
|
if (isAdd || op is IOpCodeCbuf || op is IOpCodeImm)
|
||||||
{
|
{
|
||||||
negateA = op.RawOpCode.Extract(43);
|
negateA = op.RawOpCode.Extract(43);
|
||||||
absoluteA = op.RawOpCode.Extract(44);
|
absoluteA = op.RawOpCode.Extract(44);
|
||||||
|
@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
absoluteA = op.RawOpCode.Extract(44);
|
absoluteA = op.RawOpCode.Extract(44);
|
||||||
}
|
}
|
||||||
else if (op is OpCodeAluImm32 && op.Emitter == InstEmit.Hadd2)
|
else if (op is OpCodeAluImm32 && isAdd)
|
||||||
{
|
{
|
||||||
negateA = op.RawOpCode.Extract(56);
|
negateA = op.RawOpCode.Extract(56);
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,6 +206,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(Instruction.FP | Instruction.Ceiling, Local(), a);
|
return context.Add(Instruction.FP | Instruction.Ceiling, Local(), a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Operand FPClamp(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||||
|
{
|
||||||
|
return context.Add(Instruction.FP | Instruction.Clamp, Local(), a, b, c);
|
||||||
|
}
|
||||||
|
|
||||||
public static Operand FPCompareEqual(this EmitterContext context, Operand a, Operand b)
|
public static Operand FPCompareEqual(this EmitterContext context, Operand a, Operand b)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.FP | Instruction.CompareEqual, Local(), a, b);
|
return context.Add(Instruction.FP | Instruction.CompareEqual, Local(), a, b);
|
||||||
|
|
Loading…
Reference in a new issue