 a7109c767b
			
		
	
	
		a7109c767b
		
			
		
	
	
	
	
		
			
			* Rewrite shader decoding stage * Fix P2R constant buffer encoding * Fix PSET/PSETP * PR feedback * Log unimplemented shader instructions * Implement NOP * Remove using * PR feedback
		
			
				
	
	
		
			400 lines
		
	
	
		
			No EOL
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			No EOL
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Ryujinx.Graphics.Shader.Decoders;
 | |
| using Ryujinx.Graphics.Shader.IntermediateRepresentation;
 | |
| using Ryujinx.Graphics.Shader.Translation;
 | |
| using System;
 | |
| 
 | |
| using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
 | |
| using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
 | |
| using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
 | |
| 
 | |
| namespace Ryujinx.Graphics.Shader.Instructions
 | |
| {
 | |
|     static partial class InstEmit
 | |
|     {
 | |
|         public static void F2fR(EmitterContext context)
 | |
|         {
 | |
|             InstF2fR op = context.GetOp<InstF2fR>();
 | |
| 
 | |
|             var src = UnpackReg(context, op.SrcFmt, op.Sh, op.SrcB);
 | |
| 
 | |
|             EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
 | |
|         }
 | |
| 
 | |
|         public static void F2fI(EmitterContext context)
 | |
|         {
 | |
|             InstF2fI op = context.GetOp<InstF2fI>();
 | |
| 
 | |
|             var src = UnpackImm(context, op.SrcFmt, op.Sh, Imm20ToFloat(op.Imm20));
 | |
| 
 | |
|             EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
 | |
|         }
 | |
| 
 | |
|         public static void F2fC(EmitterContext context)
 | |
|         {
 | |
|             InstF2fC op = context.GetOp<InstF2fC>();
 | |
| 
 | |
|             var src = UnpackCbuf(context, op.SrcFmt, op.Sh, op.CbufSlot, op.CbufOffset);
 | |
| 
 | |
|             EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
 | |
|         }
 | |
| 
 | |
|         public static void F2iR(EmitterContext context)
 | |
|         {
 | |
|             InstF2iR op = context.GetOp<InstF2iR>();
 | |
| 
 | |
|             var src = UnpackReg(context, op.SrcFmt, op.Sh, op.SrcB);
 | |
| 
 | |
|             EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
 | |
|         }
 | |
| 
 | |
|         public static void F2iI(EmitterContext context)
 | |
|         {
 | |
|             InstF2iI op = context.GetOp<InstF2iI>();
 | |
| 
 | |
|             var src = UnpackImm(context, op.SrcFmt, op.Sh, Imm20ToFloat(op.Imm20));
 | |
| 
 | |
|             EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
 | |
|         }
 | |
| 
 | |
|         public static void F2iC(EmitterContext context)
 | |
|         {
 | |
|             InstF2iC op = context.GetOp<InstF2iC>();
 | |
| 
 | |
|             var src = UnpackCbuf(context, op.SrcFmt, op.Sh, op.CbufSlot, op.CbufOffset);
 | |
| 
 | |
|             EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
 | |
|         }
 | |
| 
 | |
|         public static void I2fR(EmitterContext context)
 | |
|         {
 | |
|             InstI2fR op = context.GetOp<InstI2fR>();
 | |
| 
 | |
|             var src = GetSrcReg(context, op.SrcB);
 | |
| 
 | |
|             EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
 | |
|         }
 | |
| 
 | |
|         public static void I2fI(EmitterContext context)
 | |
|         {
 | |
|             InstI2fI op = context.GetOp<InstI2fI>();
 | |
| 
 | |
|             var src = GetSrcImm(context, Imm20ToSInt(op.Imm20));
 | |
| 
 | |
|             EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
 | |
|         }
 | |
| 
 | |
|         public static void I2fC(EmitterContext context)
 | |
|         {
 | |
|             InstI2fC op = context.GetOp<InstI2fC>();
 | |
| 
 | |
|             var src = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
 | |
| 
 | |
|             EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
 | |
|         }
 | |
| 
 | |
|         public static void I2iR(EmitterContext context)
 | |
|         {
 | |
|             InstI2iR op = context.GetOp<InstI2iR>();
 | |
| 
 | |
|             var src = GetSrcReg(context, op.SrcB);
 | |
| 
 | |
|             EmitI2I(context, op.SrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat);
 | |
|         }
 | |
| 
 | |
|         public static void I2iI(EmitterContext context)
 | |
|         {
 | |
|             InstI2iI op = context.GetOp<InstI2iI>();
 | |
| 
 | |
|             var src = GetSrcImm(context, Imm20ToSInt(op.Imm20));
 | |
| 
 | |
|             EmitI2I(context, op.SrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat);
 | |
|         }
 | |
| 
 | |
|         public static void I2iC(EmitterContext context)
 | |
|         {
 | |
|             InstI2iC op = context.GetOp<InstI2iC>();
 | |
| 
 | |
|             var src = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
 | |
| 
 | |
|             EmitI2I(context, op.SrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat);
 | |
|         }
 | |
| 
 | |
|         private static void EmitF2F(
 | |
|             EmitterContext context,
 | |
|             DstFmt srcType,
 | |
|             DstFmt dstType,
 | |
|             IntegerRound roundingMode,
 | |
|             Operand src,
 | |
|             int rd,
 | |
|             bool absolute,
 | |
|             bool negate,
 | |
|             bool saturate)
 | |
|         {
 | |
|             Operand srcB = context.FPAbsNeg(src, absolute, negate, srcType.ToInstFPType());
 | |
| 
 | |
|             if (srcType == dstType)
 | |
|             {
 | |
|                 srcB = roundingMode switch
 | |
|                 {
 | |
|                     IntegerRound.Round => context.FPRound(srcB, srcType.ToInstFPType()),
 | |
|                     IntegerRound.Floor => context.FPFloor(srcB, srcType.ToInstFPType()),
 | |
|                     IntegerRound.Ceil => context.FPCeiling(srcB, srcType.ToInstFPType()),
 | |
|                     IntegerRound.Trunc => context.FPTruncate(srcB, srcType.ToInstFPType()),
 | |
|                     _ => srcB
 | |
|                 };
 | |
|             }
 | |
| 
 | |
|             // We don't need to handle conversions between FP16 <-> FP32
 | |
|             // since we do FP16 operations as FP32 directly.
 | |
|             // FP16 <-> FP64 conversions are invalid.
 | |
|             if (srcType == DstFmt.F32 && dstType == DstFmt.F64)
 | |
|             {
 | |
|                 srcB = context.FP32ConvertToFP64(srcB);
 | |
|             }
 | |
|             else if (srcType == DstFmt.F64 && dstType == DstFmt.F32)
 | |
|             {
 | |
|                 srcB = context.FP64ConvertToFP32(srcB);
 | |
|             }
 | |
| 
 | |
|             srcB = context.FPSaturate(srcB, saturate, dstType.ToInstFPType());
 | |
| 
 | |
|             WriteFP(context, dstType, srcB, rd);
 | |
| 
 | |
|             // TODO: CC.
 | |
|         }
 | |
| 
 | |
|         private static void EmitF2I(
 | |
|             EmitterContext context,
 | |
|             DstFmt srcType,
 | |
|             IDstFmt dstType,
 | |
|             RoundMode2 roundingMode,
 | |
|             Operand src,
 | |
|             int rd,
 | |
|             bool absolute,
 | |
|             bool negate)
 | |
|         {
 | |
|             if (dstType == IDstFmt.U64)
 | |
|             {
 | |
|                 context.Config.GpuAccessor.Log("Unimplemented 64-bits F2I.");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             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);
 | |
| 
 | |
|             srcB = roundingMode switch
 | |
|             {
 | |
|                 RoundMode2.Round => context.FPRound(srcB),
 | |
|                 RoundMode2.Floor => context.FPFloor(srcB),
 | |
|                 RoundMode2.Ceil => context.FPCeiling(srcB),
 | |
|                 RoundMode2.Trunc => context.FPTruncate(srcB),
 | |
|                 _ => srcB
 | |
|             };
 | |
| 
 | |
|             if (!isSignedInt)
 | |
|             {
 | |
|                 // Negative float to uint cast is undefined, so we clamp the value before conversion.
 | |
|                 srcB = context.FPMaximum(srcB, ConstF(0));
 | |
|             }
 | |
| 
 | |
|             srcB = isSignedInt ? context.FPConvertToS32(srcB) : context.FPConvertToU32(srcB);
 | |
| 
 | |
|             if (isSmallInt)
 | |
|             {
 | |
|                 int min = (int)GetIntMin(dstType);
 | |
|                 int max = (int)GetIntMax(dstType);
 | |
| 
 | |
|                 srcB = isSignedInt
 | |
|                     ? context.IClampS32(srcB, Const(min), Const(max))
 | |
|                     : context.IClampU32(srcB, Const(min), Const(max));
 | |
|             }
 | |
| 
 | |
|             Operand dest = GetDest(rd);
 | |
| 
 | |
|             context.Copy(dest, srcB);
 | |
| 
 | |
|             // TODO: CC.
 | |
|         }
 | |
| 
 | |
|         private static void EmitI2F(
 | |
|             EmitterContext context,
 | |
|             ISrcFmt srcType,
 | |
|             DstFmt dstType,
 | |
|             Operand src,
 | |
|             ByteSel byteSelection,
 | |
|             int rd,
 | |
|             bool absolute,
 | |
|             bool negate)
 | |
|         {
 | |
|             bool isSignedInt =
 | |
|                 srcType == ISrcFmt.S8 ||
 | |
|                 srcType == ISrcFmt.S16 ||
 | |
|                 srcType == ISrcFmt.S32 ||
 | |
|                 srcType == ISrcFmt.S64;
 | |
|             bool isSmallInt =
 | |
|                 srcType == ISrcFmt.U16 ||
 | |
|                 srcType == ISrcFmt.S16 ||
 | |
|                 srcType == ISrcFmt.U8 ||
 | |
|                 srcType == ISrcFmt.S8;
 | |
| 
 | |
|             // TODO: Handle S/U64.
 | |
| 
 | |
|             Operand srcB = context.IAbsNeg(src, absolute, negate);
 | |
| 
 | |
|             if (isSmallInt)
 | |
|             {
 | |
|                 int size = srcType == ISrcFmt.U16 || srcType == ISrcFmt.S16 ? 16 : 8;
 | |
| 
 | |
|                 srcB = isSignedInt
 | |
|                     ? context.BitfieldExtractS32(srcB, Const((int)byteSelection * 8), Const(size))
 | |
|                     : context.BitfieldExtractU32(srcB, Const((int)byteSelection * 8), Const(size));
 | |
|             }
 | |
| 
 | |
|             srcB = isSignedInt ? context.IConvertS32ToFP(srcB) : context.IConvertU32ToFP(srcB);
 | |
| 
 | |
|             WriteFP(context, dstType, srcB, rd);
 | |
| 
 | |
|             // TODO: CC.
 | |
|         }
 | |
| 
 | |
|         private static void EmitI2I(
 | |
|             EmitterContext context,
 | |
|             ISrcFmt srcType,
 | |
|             IDstFmt dstType,
 | |
|             Operand src,
 | |
|             ByteSel byteSelection,
 | |
|             int rd,
 | |
|             bool absolute,
 | |
|             bool negate,
 | |
|             bool saturate)
 | |
|         {
 | |
|             if (srcType == ISrcFmt.U64 || dstType == IDstFmt.U64)
 | |
|             {
 | |
|                 context.Config.GpuAccessor.Log("Invalid I2I encoding.");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             bool srcIsSignedInt =
 | |
|                 srcType == ISrcFmt.S8 ||
 | |
|                 srcType == ISrcFmt.S16 ||
 | |
|                 srcType == ISrcFmt.S32 ||
 | |
|                 srcType == ISrcFmt.S64;
 | |
|             bool dstIsSignedInt =
 | |
|                 dstType == IDstFmt.S16 ||
 | |
|                 dstType == IDstFmt.S32 ||
 | |
|                 dstType == IDstFmt.S64;
 | |
|             bool srcIsSmallInt =
 | |
|                 srcType == ISrcFmt.U16 ||
 | |
|                 srcType == ISrcFmt.S16 ||
 | |
|                 srcType == ISrcFmt.U8 ||
 | |
|                 srcType == ISrcFmt.S8;
 | |
| 
 | |
|             if (srcIsSmallInt)
 | |
|             {
 | |
|                 int size = srcType == ISrcFmt.U16 || srcType == ISrcFmt.S16 ? 16 : 8;
 | |
| 
 | |
|                 src = srcIsSignedInt
 | |
|                     ? context.BitfieldExtractS32(src, Const((int)byteSelection * 8), Const(size))
 | |
|                     : context.BitfieldExtractU32(src, Const((int)byteSelection * 8), Const(size));
 | |
|             }
 | |
| 
 | |
|             src = context.IAbsNeg(src, absolute, negate);
 | |
| 
 | |
|             if (saturate)
 | |
|             {
 | |
|                 int min = (int)GetIntMin(dstType);
 | |
|                 int max = (int)GetIntMax(dstType);
 | |
| 
 | |
|                 src = dstIsSignedInt
 | |
|                     ? context.IClampS32(src, Const(min), Const(max))
 | |
|                     : context.IClampU32(src, Const(min), Const(max));
 | |
|             }
 | |
| 
 | |
|             context.Copy(GetDest(rd), src);
 | |
| 
 | |
|             // TODO: CC.
 | |
|         }
 | |
| 
 | |
|         private static Operand UnpackReg(EmitterContext context, DstFmt floatType, bool h, int reg)
 | |
|         {
 | |
|             if (floatType == DstFmt.F32)
 | |
|             {
 | |
|                 return GetSrcReg(context, reg);
 | |
|             }
 | |
|             else if (floatType == DstFmt.F16)
 | |
|             {
 | |
|                 return GetHalfUnpacked(context, GetSrcReg(context, reg), HalfSwizzle.F16)[h ? 1 : 0];
 | |
|             }
 | |
|             else if (floatType == DstFmt.F64)
 | |
|             {
 | |
|                 return GetSrcReg(context, reg, isFP64: true);
 | |
|             }
 | |
| 
 | |
|             throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
 | |
|         }
 | |
| 
 | |
|         private static Operand UnpackCbuf(EmitterContext context, DstFmt floatType, bool h, int cbufSlot, int cbufOffset)
 | |
|         {
 | |
|             if (floatType == DstFmt.F32)
 | |
|             {
 | |
|                 return GetSrcCbuf(context, cbufSlot, cbufOffset);
 | |
|             }
 | |
|             else if (floatType == DstFmt.F16)
 | |
|             {
 | |
|                 return GetHalfUnpacked(context, GetSrcCbuf(context, cbufSlot, cbufOffset), HalfSwizzle.F16)[h ? 1 : 0];
 | |
|             }
 | |
|             else if (floatType == DstFmt.F64)
 | |
|             {
 | |
|                 return GetSrcCbuf(context, cbufSlot, cbufOffset, isFP64: true);
 | |
|             }
 | |
| 
 | |
|             throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
 | |
|         }
 | |
| 
 | |
|         private static Operand UnpackImm(EmitterContext context, DstFmt floatType, bool h, int imm)
 | |
|         {
 | |
|             if (floatType == DstFmt.F32)
 | |
|             {
 | |
|                 return GetSrcImm(context, imm);
 | |
|             }
 | |
|             else if (floatType == DstFmt.F16)
 | |
|             {
 | |
|                 return GetHalfUnpacked(context, GetSrcImm(context, imm), HalfSwizzle.F16)[h ? 1 : 0];
 | |
|             }
 | |
|             else if (floatType == DstFmt.F64)
 | |
|             {
 | |
|                 return GetSrcImm(context, imm, isFP64: true);
 | |
|             }
 | |
| 
 | |
|             throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
 | |
|         }
 | |
| 
 | |
|         private static void WriteFP(EmitterContext context, DstFmt type, Operand srcB, int rd)
 | |
|         {
 | |
|             Operand dest = GetDest(rd);
 | |
| 
 | |
|             if (type == DstFmt.F32)
 | |
|             {
 | |
|                 context.Copy(dest, srcB);
 | |
|             }
 | |
|             else if (type == DstFmt.F16)
 | |
|             {
 | |
|                 context.Copy(dest, context.PackHalf2x16(srcB, ConstF(0)));
 | |
|             }
 | |
|             else /* if (type == FPType.FP64) */
 | |
|             {
 | |
|                 Operand dest2 = GetDest2(rd);
 | |
| 
 | |
|                 context.Copy(dest, context.UnpackDouble2x32Low(srcB));
 | |
|                 context.Copy(dest2, context.UnpackDouble2x32High(srcB));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private static Instruction ToInstFPType(this DstFmt type)
 | |
|         {
 | |
|             return type == DstFmt.F64 ? Instruction.FP64 : Instruction.FP32;
 | |
|         }
 | |
|     }
 | |
| } |