diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index d84a582008..599c674f34 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -89,6 +89,9 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10)); Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32)); 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("0111111x0xxxxx", InstEmit.Hsetp2, typeof(OpCodeHsetImm2x10)); Set("0101110100100x", InstEmit.Hsetp2, typeof(OpCodeSetReg)); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs index 5c4f539885..e283c3b77b 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { static class InstEmitAluHelper { - public static int GetIntMin(IntegerType type) + public static long GetIntMin(IntegerType type) { switch (type) { @@ -18,14 +18,14 @@ namespace Ryujinx.Graphics.Shader.Instructions case IntegerType.S8: return sbyte.MinValue; case IntegerType.U16: return ushort.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; } 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) { @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case IntegerType.S8: return sbyte.MaxValue; case IntegerType.U16: return ushort.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; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs index 5aa925d9c9..b152415280 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs @@ -14,18 +14,16 @@ namespace Ryujinx.Graphics.Shader.Instructions { OpCodeFArith op = (OpCodeFArith)context.CurrOp; - FPType srcType = (FPType)op.RawOpCode.Extract(8, 2); - FPType dstType = (FPType)op.RawOpCode.Extract(10, 2); + FPType dstType = (FPType)op.RawOpCode.Extract(8, 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 absoluteB = op.RawOpCode.Extract(49); - pass &= op.RoundingMode == RoundingMode.TowardsNegativeInfinity; - Operand srcB = context.FPAbsNeg(GetSrcB(context, srcType), absoluteB, negateB); - if (!pass) + if (round) { switch (op.RoundingMode) { @@ -84,6 +82,10 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (op.RoundingMode) { + case RoundingMode.ToNearest: + srcB = context.FPRound(srcB); + break; + case RoundingMode.TowardsNegativeInfinity: srcB = context.FPFloor(srcB); break; @@ -97,19 +99,13 @@ namespace Ryujinx.Graphics.Shader.Instructions break; } + long min = GetIntMin(intType); + long max = GetIntMax(intType); + + srcB = context.FPClamp(srcB, ConstF(min), ConstF(max)); + 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); context.Copy(dest, srcB); @@ -194,8 +190,8 @@ namespace Ryujinx.Graphics.Shader.Instructions dstType |= IntegerType.S8; } - int min = GetIntMin(dstType); - int max = GetIntMax(dstType); + int min = (int)GetIntMin(dstType); + int max = (int)GetIntMax(dstType); srcB = dstIsSignedInt ? context.IClampS32(srcB, Const(min), Const(max)) diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs index 79d92c2d7b..3b8d7305b2 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs @@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.Shader.Instructions bool saturate = op.RawOpCode.Extract(op is IOpCodeReg ? 32 : 52); - Operand[] srcA = GetHalfSrcA(context); + Operand[] srcA = GetHalfSrcA(context, isAdd); Operand[] srcB = GetHalfSrcB(context); Operand[] res = new Operand[2]; @@ -254,13 +254,17 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(context), GetHalfPacked(context, res)); } - public static void Hsetp2(EmitterContext context) + public static void Hset2(EmitterContext context) { 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(49, 4); @@ -269,8 +273,49 @@ namespace Ryujinx.Graphics.Shader.Instructions 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); + 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 p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]); @@ -280,6 +325,8 @@ namespace Ryujinx.Graphics.Shader.Instructions p1Res = context.BitwiseNot(p0Res); } + Operand pred = GetPredicate39(context); + p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred); p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs index 4b85e96fdc..22e2a14004 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: Warn about invalid floating point type. - return Const(0); + return ConstF(0); } 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}\"."); } - public static Operand[] GetHalfSrcA(EmitterContext context) + public static Operand[] GetHalfSrcA(EmitterContext context, bool isAdd = false) { OpCode op = context.CurrOp; 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); absoluteA = op.RawOpCode.Extract(44); @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { 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); } diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index e94d4d2da9..c55e343097 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -206,6 +206,11 @@ namespace Ryujinx.Graphics.Shader.Translation 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) { return context.Add(Instruction.FP | Instruction.CompareEqual, Local(), a, b);