using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions { static partial class InstEmit { public static void F2F(EmitterContext context) { OpCodeFArith op = (OpCodeFArith)context.CurrOp; FPType dstType = (FPType)op.RawOpCode.Extract(8, 2); FPType srcType = (FPType)op.RawOpCode.Extract(10, 2); bool round = op.RawOpCode.Extract(42); bool negateB = op.RawOpCode.Extract(45); bool absoluteB = op.RawOpCode.Extract(49); Operand srcB = context.FPAbsNeg(GetSrcB(context, srcType), absoluteB, negateB); if (round) { switch (op.RoundingMode) { case RoundingMode.ToNearest: srcB = context.FPRound(srcB); break; case RoundingMode.TowardsNegativeInfinity: srcB = context.FPFloor(srcB); break; case RoundingMode.TowardsPositiveInfinity: srcB = context.FPCeiling(srcB); break; case RoundingMode.TowardsZero: srcB = context.FPTruncate(srcB); break; } } srcB = context.FPSaturate(srcB, op.Saturate); WriteFP(context, dstType, srcB); // TODO: CC. } public static void F2I(EmitterContext context) { OpCodeFArith op = (OpCodeFArith)context.CurrOp; IntegerType intType = (IntegerType)op.RawOpCode.Extract(8, 2); if (intType == IntegerType.U64) { context.Config.PrintLog("Unimplemented 64-bits F2I."); return; } bool isSmallInt = intType <= IntegerType.U16; FPType floatType = (FPType)op.RawOpCode.Extract(10, 2); bool isSignedInt = op.RawOpCode.Extract(12); bool negateB = op.RawOpCode.Extract(45); bool absoluteB = op.RawOpCode.Extract(49); if (isSignedInt) { intType |= IntegerType.S8; } Operand srcB = context.FPAbsNeg(GetSrcB(context, floatType), absoluteB, negateB); switch (op.RoundingMode) { case RoundingMode.ToNearest: srcB = context.FPRound(srcB); break; case RoundingMode.TowardsNegativeInfinity: srcB = context.FPFloor(srcB); break; case RoundingMode.TowardsPositiveInfinity: srcB = context.FPCeiling(srcB); break; case RoundingMode.TowardsZero: srcB = context.FPTruncate(srcB); break; } 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(intType); int max = (int)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); // TODO: CC. } public static void I2F(EmitterContext context) { OpCodeAlu op = (OpCodeAlu)context.CurrOp; FPType dstType = (FPType)op.RawOpCode.Extract(8, 2); IntegerType srcType = (IntegerType)op.RawOpCode.Extract(10, 2); // TODO: Handle S/U64. bool isSmallInt = srcType <= IntegerType.U16; bool isSignedInt = op.RawOpCode.Extract(13); bool negateB = op.RawOpCode.Extract(45); bool absoluteB = op.RawOpCode.Extract(49); Operand srcB = context.IAbsNeg(GetSrcB(context), absoluteB, negateB); if (isSmallInt) { int size = srcType == IntegerType.U16 ? 16 : 8; srcB = isSignedInt ? context.BitfieldExtractS32(srcB, Const(op.ByteSelection * 8), Const(size)) : context.BitfieldExtractU32(srcB, Const(op.ByteSelection * 8), Const(size)); } srcB = isSignedInt ? context.IConvertS32ToFP(srcB) : context.IConvertU32ToFP(srcB); WriteFP(context, dstType, srcB); // TODO: CC. } public static void I2I(EmitterContext context) { OpCodeAlu op = (OpCodeAlu)context.CurrOp; IntegerType dstType = (IntegerType)op.RawOpCode.Extract(8, 2); IntegerType srcType = (IntegerType)op.RawOpCode.Extract(10, 2); if (srcType == IntegerType.U64 || dstType == IntegerType.U64) { context.Config.PrintLog("Invalid I2I encoding."); return; } bool srcIsSmallInt = srcType <= IntegerType.U16; bool dstIsSignedInt = op.RawOpCode.Extract(12); bool srcIsSignedInt = op.RawOpCode.Extract(13); bool negateB = op.RawOpCode.Extract(45); bool absoluteB = op.RawOpCode.Extract(49); Operand srcB = GetSrcB(context); if (srcIsSmallInt) { int size = srcType == IntegerType.U16 ? 16 : 8; srcB = srcIsSignedInt ? context.BitfieldExtractS32(srcB, Const(op.ByteSelection * 8), Const(size)) : context.BitfieldExtractU32(srcB, Const(op.ByteSelection * 8), Const(size)); } srcB = context.IAbsNeg(srcB, absoluteB, negateB); if (op.Saturate) { if (dstIsSignedInt) { dstType |= IntegerType.S8; } int min = (int)GetIntMin(dstType); int max = (int)GetIntMax(dstType); srcB = dstIsSignedInt ? context.IClampS32(srcB, Const(min), Const(max)) : context.IClampU32(srcB, Const(min), Const(max)); } context.Copy(GetDest(context), srcB); // TODO: CC. } private static void WriteFP(EmitterContext context, FPType type, Operand srcB) { Operand dest = GetDest(context); if (type == FPType.FP32) { context.Copy(dest, srcB); } else if (type == FPType.FP16) { context.Copy(dest, context.PackHalf2x16(srcB, ConstF(0))); } else { // TODO. } } } }