From b9d83cc97ee1cb8c60d9b01c162bab742567fe6e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 14 Nov 2021 21:37:07 -0300 Subject: [PATCH] Fix shader integer from/to double conversion (#2831) --- .../Glsl/Instructions/InstGenHelper.cs | 12 ++++-- .../Instructions/InstEmitConversion.cs | 40 +++++++++++++++---- .../Instructions/InstEmitTexture.cs | 2 +- .../IntermediateRepresentation/Instruction.cs | 12 ++++-- .../StructuredIr/InstructionInfo.cs | 12 ++++-- .../Translation/EmitterContextInsts.cs | 40 ++++++++++++++----- .../Translation/Rewriter.cs | 6 +-- 7 files changed, 90 insertions(+), 34 deletions(-) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 1fa253992..a52e70c31 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -53,10 +53,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12); Add(Instruction.ConvertFP32ToFP64, InstType.CallUnary, "double"); Add(Instruction.ConvertFP64ToFP32, InstType.CallUnary, "float"); - Add(Instruction.ConvertFPToS32, InstType.CallUnary, "int"); - Add(Instruction.ConvertFPToU32, InstType.CallUnary, "uint"); - Add(Instruction.ConvertS32ToFP, InstType.CallUnary, "float"); - Add(Instruction.ConvertU32ToFP, InstType.CallUnary, "float"); + Add(Instruction.ConvertFP32ToS32, InstType.CallUnary, "int"); + Add(Instruction.ConvertFP32ToU32, InstType.CallUnary, "uint"); + Add(Instruction.ConvertFP64ToS32, InstType.CallUnary, "int"); + Add(Instruction.ConvertFP64ToU32, InstType.CallUnary, "uint"); + Add(Instruction.ConvertS32ToFP32, InstType.CallUnary, "float"); + Add(Instruction.ConvertS32ToFP64, InstType.CallUnary, "double"); + Add(Instruction.ConvertU32ToFP32, InstType.CallUnary, "float"); + Add(Instruction.ConvertU32ToFP64, InstType.CallUnary, "double"); Add(Instruction.Cosine, InstType.CallUnary, "cos"); Add(Instruction.Ddx, InstType.CallUnary, "dFdx"); Add(Instruction.Ddy, InstType.CallUnary, "dFdy"); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs index 01cdc4454..62124554b 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs @@ -179,27 +179,40 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } + Instruction fpType = srcType.ToInstFPType(); + bool isSignedInt = dstType == IDstFmt.S16 || dstType == IDstFmt.S32 || dstType == IDstFmt.S64; bool isSmallInt = dstType == IDstFmt.U16 || dstType == IDstFmt.S16; - Operand srcB = context.FPAbsNeg(src, absolute, negate); + Operand srcB = context.FPAbsNeg(src, absolute, negate, fpType); srcB = roundingMode switch { - RoundMode2.Round => context.FPRound(srcB), - RoundMode2.Floor => context.FPFloor(srcB), - RoundMode2.Ceil => context.FPCeiling(srcB), - RoundMode2.Trunc => context.FPTruncate(srcB), + RoundMode2.Round => context.FPRound(srcB, fpType), + RoundMode2.Floor => context.FPFloor(srcB, fpType), + RoundMode2.Ceil => context.FPCeiling(srcB, fpType), + RoundMode2.Trunc => context.FPTruncate(srcB, fpType), _ => srcB }; if (!isSignedInt) { // Negative float to uint cast is undefined, so we clamp the value before conversion. - srcB = context.FPMaximum(srcB, ConstF(0)); + srcB = context.FPMaximum(srcB, ConstF(0), fpType); } - srcB = isSignedInt ? context.FPConvertToS32(srcB) : context.FPConvertToU32(srcB); + if (srcType == DstFmt.F64) + { + srcB = isSignedInt + ? context.FP64ConvertToS32(srcB) + : context.FP64ConvertToU32(srcB); + } + else + { + srcB = isSignedInt + ? context.FP32ConvertToS32(srcB) + : context.FP32ConvertToU32(srcB); + } if (isSmallInt) { @@ -252,7 +265,18 @@ namespace Ryujinx.Graphics.Shader.Instructions : context.BitfieldExtractU32(srcB, Const((int)byteSelection * 8), Const(size)); } - srcB = isSignedInt ? context.IConvertS32ToFP(srcB) : context.IConvertU32ToFP(srcB); + if (dstType == DstFmt.F64) + { + srcB = isSignedInt + ? context.IConvertS32ToFP64(srcB) + : context.IConvertU32ToFP64(srcB); + } + else + { + srcB = isSignedInt + ? context.IConvertS32ToFP32(srcB) + : context.IConvertU32ToFP32(srcB); + } WriteFP(context, dstType, srcB, rd); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index ba1fdf17b..33cc6af7c 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -939,7 +939,7 @@ namespace Ryujinx.Graphics.Shader.Instructions tempDest = context.FPMultiply(tempDest, ConstF(256.0f)); - Operand fixedPointValue = context.FPConvertToS32(tempDest); + Operand fixedPointValue = context.FP32ConvertToS32(tempDest); context.Copy(GetDest(), fixedPointValue); } diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 791c8f11c..c3fb28827 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -49,10 +49,14 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ConditionalSelect, ConvertFP32ToFP64, ConvertFP64ToFP32, - ConvertFPToS32, - ConvertFPToU32, - ConvertS32ToFP, - ConvertU32ToFP, + ConvertFP32ToS32, + ConvertFP32ToU32, + ConvertFP64ToS32, + ConvertFP64ToU32, + ConvertS32ToFP32, + ConvertS32ToFP64, + ConvertU32ToFP32, + ConvertU32ToFP64, Copy, Cosine, Ddx, diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index c647f450e..f3397adaa 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -66,10 +66,14 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ConditionalSelect, VariableType.Scalar, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); Add(Instruction.ConvertFP32ToFP64, VariableType.F64, VariableType.F32); Add(Instruction.ConvertFP64ToFP32, VariableType.F32, VariableType.F64); - Add(Instruction.ConvertFPToS32, VariableType.S32, VariableType.F32); - Add(Instruction.ConvertFPToU32, VariableType.U32, VariableType.F32); - Add(Instruction.ConvertS32ToFP, VariableType.F32, VariableType.S32); - Add(Instruction.ConvertU32ToFP, VariableType.F32, VariableType.U32); + Add(Instruction.ConvertFP32ToS32, VariableType.S32, VariableType.F32); + Add(Instruction.ConvertFP32ToU32, VariableType.U32, VariableType.F32); + Add(Instruction.ConvertFP64ToS32, VariableType.S32, VariableType.F64); + Add(Instruction.ConvertFP64ToU32, VariableType.U32, VariableType.F64); + Add(Instruction.ConvertS32ToFP32, VariableType.F32, VariableType.S32); + Add(Instruction.ConvertS32ToFP64, VariableType.F64, VariableType.S32); + Add(Instruction.ConvertU32ToFP32, VariableType.F32, VariableType.U32); + Add(Instruction.ConvertU32ToFP64, VariableType.F64, VariableType.U32); Add(Instruction.Cosine, VariableType.Scalar, VariableType.Scalar); Add(Instruction.Ddx, VariableType.F32, VariableType.F32); Add(Instruction.Ddy, VariableType.F32, VariableType.F32); diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 5e607b44b..6baf33e1a 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -241,14 +241,24 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.CompareLess, Local(), a, b); } - public static Operand FPConvertToS32(this EmitterContext context, Operand a) + public static Operand FP32ConvertToS32(this EmitterContext context, Operand a) { - return context.Add(Instruction.ConvertFPToS32, Local(), a); + return context.Add(Instruction.ConvertFP32ToS32, Local(), a); } - public static Operand FPConvertToU32(this EmitterContext context, Operand a) + public static Operand FP32ConvertToU32(this EmitterContext context, Operand a) { - return context.Add(Instruction.ConvertFPToU32, Local(), a); + return context.Add(Instruction.ConvertFP32ToU32, Local(), a); + } + + public static Operand FP64ConvertToS32(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertFP64ToS32, Local(), a); + } + + public static Operand FP64ConvertToU32(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertFP64ToU32, Local(), a); } public static Operand FPCosine(this EmitterContext context, Operand a) @@ -281,9 +291,9 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.FP32 | Instruction.LogarithmB2, Local(), a); } - public static Operand FPMaximum(this EmitterContext context, Operand a, Operand b) + public static Operand FPMaximum(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) { - return context.Add(Instruction.FP32 | Instruction.Maximum, Local(), a, b); + return context.Add(fpType | Instruction.Maximum, Local(), a, b); } public static Operand FPMinimum(this EmitterContext context, Operand a, Operand b) @@ -461,14 +471,24 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.CompareNotEqual, Local(), a, b); } - public static Operand IConvertS32ToFP(this EmitterContext context, Operand a) + public static Operand IConvertS32ToFP32(this EmitterContext context, Operand a) { - return context.Add(Instruction.ConvertS32ToFP, Local(), a); + return context.Add(Instruction.ConvertS32ToFP32, Local(), a); } - public static Operand IConvertU32ToFP(this EmitterContext context, Operand a) + public static Operand IConvertS32ToFP64(this EmitterContext context, Operand a) { - return context.Add(Instruction.ConvertU32ToFP, Local(), a); + return context.Add(Instruction.ConvertS32ToFP64, Local(), a); + } + + public static Operand IConvertU32ToFP32(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertU32ToFP32, Local(), a); + } + + public static Operand IConvertU32ToFP64(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertU32ToFP64, Local(), a); } public static Operand IMaximumS32(this EmitterContext context, Operand a, Operand b) diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 02a0feda8..910faf1ca 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -286,7 +286,7 @@ namespace Ryujinx.Graphics.Shader.Translation { Operand res = Local(); - node.List.AddBefore(node, new Operation(Instruction.ConvertFPToS32, res, value)); + node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); return res; } @@ -295,7 +295,7 @@ namespace Ryujinx.Graphics.Shader.Translation { Operand res = Local(); - node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP, res, value)); + node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); return res; } @@ -501,7 +501,7 @@ namespace Ryujinx.Graphics.Shader.Translation // as replacement for SNORM (which is not supported). INode[] uses = texOp.Dest.UseOps.ToArray(); - Operation convOp = new Operation(Instruction.ConvertS32ToFP, Local(), texOp.Dest); + Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), texOp.Dest); Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive)); node = node.List.AddAfter(node, convOp);