InstEmitVideoMinMax.cs
1 using Ryujinx.Graphics.Shader.Decoders; 2 using Ryujinx.Graphics.Shader.IntermediateRepresentation; 3 using Ryujinx.Graphics.Shader.Translation; 4 5 using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; 6 using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; 7 8 namespace Ryujinx.Graphics.Shader.Instructions 9 { 10 static partial class InstEmit 11 { 12 public static void Vmnmx(EmitterContext context) 13 { 14 InstVmnmx op = context.GetOp<InstVmnmx>(); 15 16 Operand srcA = InstEmitAluHelper.Extend(context, GetSrcReg(context, op.SrcA), op.ASelect); 17 Operand srcC = GetSrcReg(context, op.SrcC); 18 Operand srcB; 19 20 if (op.BVideo) 21 { 22 srcB = InstEmitAluHelper.Extend(context, GetSrcReg(context, op.SrcB), op.BSelect); 23 } 24 else 25 { 26 int imm = op.Imm16; 27 28 if ((op.BSelect & VectorSelect.S8B0) != 0) 29 { 30 imm = (imm << 16) >> 16; 31 } 32 33 srcB = Const(imm); 34 } 35 36 Operand res; 37 38 bool resSigned; 39 40 if ((op.ASelect & VectorSelect.S8B0) != (op.BSelect & VectorSelect.S8B0)) 41 { 42 // Signedness is different, but for max, result will always fit a U32, 43 // since one of the inputs can't be negative, and the result is the one 44 // with highest value. For min, it will always fit on a S32, since 45 // one of the input can't be greater than INT_MAX and we want the lowest value. 46 resSigned = !op.Mn; 47 48 res = op.Mn ? context.IMaximumU32(srcA, srcB) : context.IMinimumS32(srcA, srcB); 49 50 if ((op.ASelect & VectorSelect.S8B0) != 0) 51 { 52 Operand isBGtIntMax = context.ICompareLess(srcB, Const(0)); 53 54 res = context.ConditionalSelect(isBGtIntMax, srcB, res); 55 } 56 else 57 { 58 Operand isAGtIntMax = context.ICompareLess(srcA, Const(0)); 59 60 res = context.ConditionalSelect(isAGtIntMax, srcA, res); 61 } 62 } 63 else 64 { 65 // Ra and Rb have the same signedness, so doesn't matter which one we test. 66 resSigned = (op.ASelect & VectorSelect.S8B0) != 0; 67 68 if (op.Mn) 69 { 70 res = resSigned 71 ? context.IMaximumS32(srcA, srcB) 72 : context.IMaximumU32(srcA, srcB); 73 } 74 else 75 { 76 res = resSigned 77 ? context.IMinimumS32(srcA, srcB) 78 : context.IMinimumU32(srcA, srcB); 79 } 80 } 81 82 if (op.Sat) 83 { 84 if (op.DFormat && !resSigned) 85 { 86 res = context.IMinimumU32(res, Const(int.MaxValue)); 87 } 88 else if (!op.DFormat && resSigned) 89 { 90 res = context.IMaximumS32(res, Const(0)); 91 } 92 } 93 94 switch (op.VideoOp) 95 { 96 case VideoOp.Acc: 97 res = context.IAdd(res, srcC); 98 break; 99 case VideoOp.Max: 100 res = op.DFormat ? context.IMaximumS32(res, srcC) : context.IMaximumU32(res, srcC); 101 break; 102 case VideoOp.Min: 103 res = op.DFormat ? context.IMinimumS32(res, srcC) : context.IMinimumU32(res, srcC); 104 break; 105 case VideoOp.Mrg16h: 106 res = context.BitfieldInsert(srcC, res, Const(16), Const(16)); 107 break; 108 case VideoOp.Mrg16l: 109 res = context.BitfieldInsert(srcC, res, Const(0), Const(16)); 110 break; 111 case VideoOp.Mrg8b0: 112 res = context.BitfieldInsert(srcC, res, Const(0), Const(8)); 113 break; 114 case VideoOp.Mrg8b2: 115 res = context.BitfieldInsert(srcC, res, Const(16), Const(8)); 116 break; 117 } 118 119 context.Copy(GetDest(op.Dest), res); 120 } 121 122 public static void Vsetp(EmitterContext context) 123 { 124 InstVsetp op = context.GetOp<InstVsetp>(); 125 126 Operand srcA = InstEmitAluHelper.Extend(context, GetSrcReg(context, op.SrcA), op.ASelect); 127 Operand srcB; 128 129 if (op.BVideo) 130 { 131 srcB = InstEmitAluHelper.Extend(context, GetSrcReg(context, op.SrcB), op.BSelect); 132 } 133 else 134 { 135 int imm = op.Imm16; 136 137 if ((op.BSelect & VectorSelect.S8B0) != 0) 138 { 139 imm = (imm << 16) >> 16; 140 } 141 142 srcB = Const(imm); 143 } 144 145 Operand p0Res; 146 147 bool signedA = (op.ASelect & VectorSelect.S8B0) != 0; 148 bool signedB = (op.BSelect & VectorSelect.S8B0) != 0; 149 150 if (signedA != signedB) 151 { 152 bool a32 = (op.ASelect & ~VectorSelect.S8B0) == VectorSelect.U32; 153 bool b32 = (op.BSelect & ~VectorSelect.S8B0) == VectorSelect.U32; 154 155 if (!a32 && !b32) 156 { 157 // Both values are extended small integer and can always fit in a S32, just do a signed comparison. 158 p0Res = GetIntComparison(context, op.VComp, srcA, srcB, isSigned: true, extended: false); 159 } 160 else 161 { 162 // TODO: Mismatching sign case. 163 p0Res = Const(0); 164 } 165 } 166 else 167 { 168 // Sign matches, just do a regular comparison. 169 p0Res = GetIntComparison(context, op.VComp, srcA, srcB, signedA, extended: false); 170 } 171 172 Operand p1Res = context.BitwiseNot(p0Res); 173 174 Operand pred = GetPredicate(context, op.SrcPred, op.SrcPredInv); 175 176 p0Res = InstEmitAluHelper.GetPredLogicalOp(context, op.BoolOp, p0Res, pred); 177 p1Res = InstEmitAluHelper.GetPredLogicalOp(context, op.BoolOp, p1Res, pred); 178 179 context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res); 180 context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); 181 } 182 } 183 }