InstEmitConversion.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. using Ryujinx.Graphics.Shader.Decoders;
  2. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using System;
  5. using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
  6. using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
  7. using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  8. namespace Ryujinx.Graphics.Shader.Instructions
  9. {
  10. static partial class InstEmit
  11. {
  12. public static void F2fR(EmitterContext context)
  13. {
  14. InstF2fR op = context.GetOp<InstF2fR>();
  15. var src = UnpackReg(context, op.SrcFmt, op.Sh, op.SrcB);
  16. EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
  17. }
  18. public static void F2fI(EmitterContext context)
  19. {
  20. InstF2fI op = context.GetOp<InstF2fI>();
  21. var src = UnpackImm(context, op.SrcFmt, op.Sh, Imm20ToFloat(op.Imm20));
  22. EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
  23. }
  24. public static void F2fC(EmitterContext context)
  25. {
  26. InstF2fC op = context.GetOp<InstF2fC>();
  27. var src = UnpackCbuf(context, op.SrcFmt, op.Sh, op.CbufSlot, op.CbufOffset);
  28. EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
  29. }
  30. public static void F2iR(EmitterContext context)
  31. {
  32. InstF2iR op = context.GetOp<InstF2iR>();
  33. var src = UnpackReg(context, op.SrcFmt, op.Sh, op.SrcB);
  34. EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
  35. }
  36. public static void F2iI(EmitterContext context)
  37. {
  38. InstF2iI op = context.GetOp<InstF2iI>();
  39. var src = UnpackImm(context, op.SrcFmt, op.Sh, Imm20ToFloat(op.Imm20));
  40. EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
  41. }
  42. public static void F2iC(EmitterContext context)
  43. {
  44. InstF2iC op = context.GetOp<InstF2iC>();
  45. var src = UnpackCbuf(context, op.SrcFmt, op.Sh, op.CbufSlot, op.CbufOffset);
  46. EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
  47. }
  48. public static void I2fR(EmitterContext context)
  49. {
  50. InstI2fR op = context.GetOp<InstI2fR>();
  51. var src = GetSrcReg(context, op.SrcB);
  52. EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
  53. }
  54. public static void I2fI(EmitterContext context)
  55. {
  56. InstI2fI op = context.GetOp<InstI2fI>();
  57. var src = GetSrcImm(context, Imm20ToSInt(op.Imm20));
  58. EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
  59. }
  60. public static void I2fC(EmitterContext context)
  61. {
  62. InstI2fC op = context.GetOp<InstI2fC>();
  63. var src = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
  64. EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
  65. }
  66. public static void I2iR(EmitterContext context)
  67. {
  68. InstI2iR op = context.GetOp<InstI2iR>();
  69. var src = GetSrcReg(context, op.SrcB);
  70. EmitI2I(context, op.ISrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat, op.WriteCC);
  71. }
  72. public static void I2iI(EmitterContext context)
  73. {
  74. InstI2iI op = context.GetOp<InstI2iI>();
  75. var src = GetSrcImm(context, Imm20ToSInt(op.Imm20));
  76. EmitI2I(context, op.ISrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat, op.WriteCC);
  77. }
  78. public static void I2iC(EmitterContext context)
  79. {
  80. InstI2iC op = context.GetOp<InstI2iC>();
  81. var src = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
  82. EmitI2I(context, op.ISrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat, op.WriteCC);
  83. }
  84. private static void EmitF2F(
  85. EmitterContext context,
  86. DstFmt srcType,
  87. DstFmt dstType,
  88. IntegerRound roundingMode,
  89. Operand src,
  90. int rd,
  91. bool absolute,
  92. bool negate,
  93. bool saturate)
  94. {
  95. Operand srcB = context.FPAbsNeg(src, absolute, negate, srcType.ToInstFPType());
  96. if (srcType == dstType)
  97. {
  98. srcB = roundingMode switch
  99. {
  100. IntegerRound.Round => context.FPRound(srcB, srcType.ToInstFPType()),
  101. IntegerRound.Floor => context.FPFloor(srcB, srcType.ToInstFPType()),
  102. IntegerRound.Ceil => context.FPCeiling(srcB, srcType.ToInstFPType()),
  103. IntegerRound.Trunc => context.FPTruncate(srcB, srcType.ToInstFPType()),
  104. _ => srcB
  105. };
  106. }
  107. // We don't need to handle conversions between FP16 <-> FP32
  108. // since we do FP16 operations as FP32 directly.
  109. // FP16 <-> FP64 conversions are invalid.
  110. if (srcType == DstFmt.F32 && dstType == DstFmt.F64)
  111. {
  112. srcB = context.FP32ConvertToFP64(srcB);
  113. }
  114. else if (srcType == DstFmt.F64 && dstType == DstFmt.F32)
  115. {
  116. srcB = context.FP64ConvertToFP32(srcB);
  117. }
  118. srcB = context.FPSaturate(srcB, saturate, dstType.ToInstFPType());
  119. WriteFP(context, dstType, srcB, rd);
  120. // TODO: CC.
  121. }
  122. private static void EmitF2I(
  123. EmitterContext context,
  124. DstFmt srcType,
  125. IDstFmt dstType,
  126. RoundMode2 roundingMode,
  127. Operand src,
  128. int rd,
  129. bool absolute,
  130. bool negate)
  131. {
  132. if (dstType == IDstFmt.U64)
  133. {
  134. context.Config.GpuAccessor.Log("Unimplemented 64-bits F2I.");
  135. }
  136. Instruction fpType = srcType.ToInstFPType();
  137. bool isSignedInt = dstType == IDstFmt.S16 || dstType == IDstFmt.S32 || dstType == IDstFmt.S64;
  138. bool isSmallInt = dstType == IDstFmt.U16 || dstType == IDstFmt.S16;
  139. Operand srcB = context.FPAbsNeg(src, absolute, negate, fpType);
  140. srcB = roundingMode switch
  141. {
  142. RoundMode2.Round => context.FPRound(srcB, fpType),
  143. RoundMode2.Floor => context.FPFloor(srcB, fpType),
  144. RoundMode2.Ceil => context.FPCeiling(srcB, fpType),
  145. RoundMode2.Trunc => context.FPTruncate(srcB, fpType),
  146. _ => srcB
  147. };
  148. if (!isSignedInt)
  149. {
  150. // Negative float to uint cast is undefined, so we clamp the value before conversion.
  151. Operand c0 = srcType == DstFmt.F64 ? context.PackDouble2x32(0.0) : ConstF(0);
  152. srcB = context.FPMaximum(srcB, c0, fpType);
  153. }
  154. if (srcType == DstFmt.F64)
  155. {
  156. srcB = isSignedInt
  157. ? context.FP64ConvertToS32(srcB)
  158. : context.FP64ConvertToU32(srcB);
  159. }
  160. else
  161. {
  162. srcB = isSignedInt
  163. ? context.FP32ConvertToS32(srcB)
  164. : context.FP32ConvertToU32(srcB);
  165. }
  166. if (isSmallInt)
  167. {
  168. int min = (int)GetIntMin(dstType);
  169. int max = (int)GetIntMax(dstType);
  170. srcB = isSignedInt
  171. ? context.IClampS32(srcB, Const(min), Const(max))
  172. : context.IClampU32(srcB, Const(min), Const(max));
  173. }
  174. Operand dest = GetDest(rd);
  175. context.Copy(dest, srcB);
  176. // TODO: CC.
  177. }
  178. private static void EmitI2F(
  179. EmitterContext context,
  180. ISrcFmt srcType,
  181. DstFmt dstType,
  182. Operand src,
  183. ByteSel byteSelection,
  184. int rd,
  185. bool absolute,
  186. bool negate)
  187. {
  188. bool isSignedInt =
  189. srcType == ISrcFmt.S8 ||
  190. srcType == ISrcFmt.S16 ||
  191. srcType == ISrcFmt.S32 ||
  192. srcType == ISrcFmt.S64;
  193. bool isSmallInt =
  194. srcType == ISrcFmt.U16 ||
  195. srcType == ISrcFmt.S16 ||
  196. srcType == ISrcFmt.U8 ||
  197. srcType == ISrcFmt.S8;
  198. // TODO: Handle S/U64.
  199. Operand srcB = context.IAbsNeg(src, absolute, negate);
  200. if (isSmallInt)
  201. {
  202. int size = srcType == ISrcFmt.U16 || srcType == ISrcFmt.S16 ? 16 : 8;
  203. srcB = isSignedInt
  204. ? context.BitfieldExtractS32(srcB, Const((int)byteSelection * 8), Const(size))
  205. : context.BitfieldExtractU32(srcB, Const((int)byteSelection * 8), Const(size));
  206. }
  207. if (dstType == DstFmt.F64)
  208. {
  209. srcB = isSignedInt
  210. ? context.IConvertS32ToFP64(srcB)
  211. : context.IConvertU32ToFP64(srcB);
  212. }
  213. else
  214. {
  215. srcB = isSignedInt
  216. ? context.IConvertS32ToFP32(srcB)
  217. : context.IConvertU32ToFP32(srcB);
  218. }
  219. WriteFP(context, dstType, srcB, rd);
  220. // TODO: CC.
  221. }
  222. private static void EmitI2I(
  223. EmitterContext context,
  224. ISrcDstFmt srcType,
  225. ISrcDstFmt dstType,
  226. Operand src,
  227. ByteSel byteSelection,
  228. int rd,
  229. bool absolute,
  230. bool negate,
  231. bool saturate,
  232. bool writeCC)
  233. {
  234. if ((srcType & ~ISrcDstFmt.S8) > ISrcDstFmt.U32 || (dstType & ~ISrcDstFmt.S8) > ISrcDstFmt.U32)
  235. {
  236. context.Config.GpuAccessor.Log("Invalid I2I encoding.");
  237. return;
  238. }
  239. bool srcIsSignedInt =
  240. srcType == ISrcDstFmt.S8 ||
  241. srcType == ISrcDstFmt.S16 ||
  242. srcType == ISrcDstFmt.S32;
  243. bool dstIsSignedInt =
  244. dstType == ISrcDstFmt.S8 ||
  245. dstType == ISrcDstFmt.S16 ||
  246. dstType == ISrcDstFmt.S32;
  247. bool srcIsSmallInt =
  248. srcType == ISrcDstFmt.U16 ||
  249. srcType == ISrcDstFmt.S16 ||
  250. srcType == ISrcDstFmt.U8 ||
  251. srcType == ISrcDstFmt.S8;
  252. if (srcIsSmallInt)
  253. {
  254. int size = srcType == ISrcDstFmt.U16 || srcType == ISrcDstFmt.S16 ? 16 : 8;
  255. src = srcIsSignedInt
  256. ? context.BitfieldExtractS32(src, Const((int)byteSelection * 8), Const(size))
  257. : context.BitfieldExtractU32(src, Const((int)byteSelection * 8), Const(size));
  258. }
  259. src = context.IAbsNeg(src, absolute, negate);
  260. if (saturate)
  261. {
  262. int min = (int)GetIntMin(dstType);
  263. int max = (int)GetIntMax(dstType);
  264. src = dstIsSignedInt
  265. ? context.IClampS32(src, Const(min), Const(max))
  266. : context.IClampU32(src, Const(min), Const(max));
  267. }
  268. context.Copy(GetDest(rd), src);
  269. SetZnFlags(context, src, writeCC);
  270. }
  271. private static Operand UnpackReg(EmitterContext context, DstFmt floatType, bool h, int reg)
  272. {
  273. if (floatType == DstFmt.F32)
  274. {
  275. return GetSrcReg(context, reg);
  276. }
  277. else if (floatType == DstFmt.F16)
  278. {
  279. return GetHalfUnpacked(context, GetSrcReg(context, reg), HalfSwizzle.F16)[h ? 1 : 0];
  280. }
  281. else if (floatType == DstFmt.F64)
  282. {
  283. return GetSrcReg(context, reg, isFP64: true);
  284. }
  285. throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
  286. }
  287. private static Operand UnpackCbuf(EmitterContext context, DstFmt floatType, bool h, int cbufSlot, int cbufOffset)
  288. {
  289. if (floatType == DstFmt.F32)
  290. {
  291. return GetSrcCbuf(context, cbufSlot, cbufOffset);
  292. }
  293. else if (floatType == DstFmt.F16)
  294. {
  295. return GetHalfUnpacked(context, GetSrcCbuf(context, cbufSlot, cbufOffset), HalfSwizzle.F16)[h ? 1 : 0];
  296. }
  297. else if (floatType == DstFmt.F64)
  298. {
  299. return GetSrcCbuf(context, cbufSlot, cbufOffset, isFP64: true);
  300. }
  301. throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
  302. }
  303. private static Operand UnpackImm(EmitterContext context, DstFmt floatType, bool h, int imm)
  304. {
  305. if (floatType == DstFmt.F32)
  306. {
  307. return GetSrcImm(context, imm);
  308. }
  309. else if (floatType == DstFmt.F16)
  310. {
  311. return GetHalfUnpacked(context, GetSrcImm(context, imm), HalfSwizzle.F16)[h ? 1 : 0];
  312. }
  313. else if (floatType == DstFmt.F64)
  314. {
  315. return GetSrcImm(context, imm, isFP64: true);
  316. }
  317. throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
  318. }
  319. private static void WriteFP(EmitterContext context, DstFmt type, Operand srcB, int rd)
  320. {
  321. Operand dest = GetDest(rd);
  322. if (type == DstFmt.F32)
  323. {
  324. context.Copy(dest, srcB);
  325. }
  326. else if (type == DstFmt.F16)
  327. {
  328. context.Copy(dest, context.PackHalf2x16(srcB, ConstF(0)));
  329. }
  330. else /* if (type == FPType.FP64) */
  331. {
  332. Operand dest2 = GetDest2(rd);
  333. context.Copy(dest, context.UnpackDouble2x32Low(srcB));
  334. context.Copy(dest2, context.UnpackDouble2x32High(srcB));
  335. }
  336. }
  337. private static Instruction ToInstFPType(this DstFmt type)
  338. {
  339. return type == DstFmt.F64 ? Instruction.FP64 : Instruction.FP32;
  340. }
  341. }
  342. }