InstEmitMemory.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. using Ryujinx.Graphics.Shader.Decoders;
  2. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
  5. using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  6. namespace Ryujinx.Graphics.Shader.Instructions
  7. {
  8. static partial class InstEmit
  9. {
  10. public static void Ald(EmitterContext context)
  11. {
  12. OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
  13. Operand primVertex = context.Copy(GetSrcC(context));
  14. for (int index = 0; index < op.Count; index++)
  15. {
  16. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  17. if (rd.IsRZ)
  18. {
  19. break;
  20. }
  21. Operand src = Attribute(op.AttributeOffset + index * 4);
  22. context.Copy(Register(rd), context.LoadAttribute(src, primVertex));
  23. }
  24. }
  25. public static void Ast(EmitterContext context)
  26. {
  27. OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
  28. for (int index = 0; index < op.Count; index++)
  29. {
  30. if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex)
  31. {
  32. break;
  33. }
  34. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  35. Operand dest = Attribute(op.AttributeOffset + index * 4);
  36. context.Copy(dest, Register(rd));
  37. }
  38. }
  39. public static void Ipa(EmitterContext context)
  40. {
  41. OpCodeIpa op = (OpCodeIpa)context.CurrOp;
  42. InterpolationQualifier iq = InterpolationQualifier.None;
  43. switch (op.Mode)
  44. {
  45. case InterpolationMode.Pass: iq = InterpolationQualifier.NoPerspective; break;
  46. }
  47. Operand srcA = Attribute(op.AttributeOffset, iq);
  48. Operand srcB = GetSrcB(context);
  49. Operand res = context.FPSaturate(srcA, op.Saturate);
  50. context.Copy(GetDest(context), res);
  51. }
  52. public static void Isberd(EmitterContext context)
  53. {
  54. // This instruction performs a load from ISBE memory,
  55. // however it seems to be only used to get some vertex
  56. // input data, so we instead propagate the offset so that
  57. // it can be used on the attribute load.
  58. context.Copy(GetDest(context), GetSrcA(context));
  59. }
  60. public static void Ld(EmitterContext context)
  61. {
  62. LoadLocalOrGlobal(context, isGlobal: false);
  63. }
  64. public static void Ldc(EmitterContext context)
  65. {
  66. OpCodeLdc op = (OpCodeLdc)context.CurrOp;
  67. if (op.Size > IntegerSize.B64)
  68. {
  69. // TODO: Warning.
  70. }
  71. bool isSmallInt = op.Size < IntegerSize.B32;
  72. int count = op.Size == IntegerSize.B64 ? 2 : 1;
  73. Operand wordOffset = context.ShiftRightU32(GetSrcA(context), Const(2));
  74. wordOffset = context.IAdd(wordOffset, Const(op.Offset));
  75. Operand bitOffset = GetBitOffset(context, GetSrcA(context));
  76. for (int index = 0; index < count; index++)
  77. {
  78. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  79. if (rd.IsRZ)
  80. {
  81. break;
  82. }
  83. Operand offset = context.IAdd(wordOffset, Const(index));
  84. Operand value = context.LoadConstant(Const(op.Slot), offset);
  85. if (isSmallInt)
  86. {
  87. value = ExtractSmallInt(context, op.Size, wordOffset, value);
  88. }
  89. context.Copy(Register(rd), value);
  90. }
  91. }
  92. public static void Ldg(EmitterContext context)
  93. {
  94. LoadLocalOrGlobal(context, isGlobal: true);
  95. }
  96. public static void Out(EmitterContext context)
  97. {
  98. OpCode op = context.CurrOp;
  99. bool emit = op.RawOpCode.Extract(39);
  100. bool cut = op.RawOpCode.Extract(40);
  101. if (!(emit || cut))
  102. {
  103. // TODO: Warning.
  104. }
  105. if (emit)
  106. {
  107. context.EmitVertex();
  108. }
  109. if (cut)
  110. {
  111. context.EndPrimitive();
  112. }
  113. }
  114. public static void St(EmitterContext context)
  115. {
  116. StoreLocalOrGlobal(context, isGlobal: false);
  117. }
  118. public static void Stg(EmitterContext context)
  119. {
  120. StoreLocalOrGlobal(context, isGlobal: true);
  121. }
  122. private static void LoadLocalOrGlobal(EmitterContext context, bool isGlobal)
  123. {
  124. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  125. if (op.Size > IntegerSize.B128)
  126. {
  127. // TODO: Warning.
  128. }
  129. bool isSmallInt = op.Size < IntegerSize.B32;
  130. int count = 1;
  131. switch (op.Size)
  132. {
  133. case IntegerSize.B64: count = 2; break;
  134. case IntegerSize.B128: count = 4; break;
  135. }
  136. Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
  137. // Word offset = byte offset / 4 (one word = 4 bytes).
  138. Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
  139. Operand bitOffset = GetBitOffset(context, baseOffset);
  140. for (int index = 0; index < count; index++)
  141. {
  142. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  143. if (rd.IsRZ)
  144. {
  145. break;
  146. }
  147. Operand offset = context.IAdd(wordOffset, Const(index));
  148. Operand value = isGlobal
  149. ? context.LoadGlobal(offset)
  150. : context.LoadLocal (offset);
  151. if (isSmallInt)
  152. {
  153. value = ExtractSmallInt(context, op.Size, bitOffset, value);
  154. }
  155. context.Copy(Register(rd), value);
  156. }
  157. }
  158. private static void StoreLocalOrGlobal(EmitterContext context, bool isGlobal)
  159. {
  160. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  161. if (op.Size > IntegerSize.B128)
  162. {
  163. // TODO: Warning.
  164. }
  165. bool isSmallInt = op.Size < IntegerSize.B32;
  166. int count = 1;
  167. switch (op.Size)
  168. {
  169. case IntegerSize.B64: count = 2; break;
  170. case IntegerSize.B128: count = 4; break;
  171. }
  172. Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
  173. Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
  174. Operand bitOffset = GetBitOffset(context, baseOffset);
  175. for (int index = 0; index < count; index++)
  176. {
  177. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  178. if (rd.IsRZ)
  179. {
  180. break;
  181. }
  182. Operand value = Register(rd);
  183. Operand offset = context.IAdd(wordOffset, Const(index));
  184. if (isSmallInt)
  185. {
  186. Operand word = isGlobal
  187. ? context.LoadGlobal(offset)
  188. : context.LoadLocal (offset);
  189. value = InsertSmallInt(context, op.Size, bitOffset, word, value);
  190. }
  191. if (isGlobal)
  192. {
  193. context.StoreGlobal(offset, value);
  194. }
  195. else
  196. {
  197. context.StoreLocal(offset, value);
  198. }
  199. }
  200. }
  201. private static Operand GetBitOffset(EmitterContext context, Operand baseOffset)
  202. {
  203. // Note: byte offset = (baseOffset & 0b11) * 8.
  204. // Addresses should be always aligned to the integer type,
  205. // so we don't need to take unaligned addresses into account.
  206. return context.ShiftLeft(context.BitwiseAnd(baseOffset, Const(3)), Const(3));
  207. }
  208. private static Operand ExtractSmallInt(
  209. EmitterContext context,
  210. IntegerSize size,
  211. Operand bitOffset,
  212. Operand value)
  213. {
  214. value = context.ShiftRightU32(value, bitOffset);
  215. switch (size)
  216. {
  217. case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break;
  218. case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break;
  219. case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break;
  220. case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break;
  221. }
  222. return value;
  223. }
  224. private static Operand InsertSmallInt(
  225. EmitterContext context,
  226. IntegerSize size,
  227. Operand bitOffset,
  228. Operand word,
  229. Operand value)
  230. {
  231. switch (size)
  232. {
  233. case IntegerSize.U8:
  234. case IntegerSize.S8:
  235. value = context.BitwiseAnd(value, Const(0xff));
  236. value = context.BitfieldInsert(word, value, bitOffset, Const(8));
  237. break;
  238. case IntegerSize.U16:
  239. case IntegerSize.S16:
  240. value = context.BitwiseAnd(value, Const(0xffff));
  241. value = context.BitfieldInsert(word, value, bitOffset, Const(16));
  242. break;
  243. }
  244. return value;
  245. }
  246. }
  247. }