InstEmitAttribute.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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 Al2p(EmitterContext context)
  11. {
  12. InstAl2p op = context.GetOp<InstAl2p>();
  13. context.Copy(GetDest(op.Dest), context.IAdd(GetSrcReg(context, op.SrcA), Const(op.Imm11)));
  14. }
  15. public static void Ald(EmitterContext context)
  16. {
  17. InstAld op = context.GetOp<InstAld>();
  18. // Some of those attributes are per invocation,
  19. // so we should ignore any primitive vertex indexing for those.
  20. bool hasPrimitiveVertex = AttributeMap.HasPrimitiveVertex(context.Config.Stage, op.O) && !op.P;
  21. if (!op.Phys)
  22. {
  23. hasPrimitiveVertex &= HasPrimitiveVertex(op.Imm11);
  24. }
  25. Operand primVertex = hasPrimitiveVertex ? context.Copy(GetSrcReg(context, op.SrcB)) : null;
  26. for (int index = 0; index < (int)op.AlSize + 1; index++)
  27. {
  28. Register rd = new Register(op.Dest + index, RegisterType.Gpr);
  29. if (rd.IsRZ)
  30. {
  31. break;
  32. }
  33. if (op.Phys)
  34. {
  35. Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
  36. Operand vecIndex = context.ShiftRightU32(offset, Const(4));
  37. Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
  38. StorageKind storageKind = op.O ? StorageKind.Output : StorageKind.Input;
  39. context.Copy(Register(rd), context.Load(storageKind, IoVariable.UserDefined, primVertex, vecIndex, elemIndex));
  40. }
  41. else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P)
  42. {
  43. int offset = FixedFuncToUserAttribute(context.Config, op.Imm11 + index * 4, op.O);
  44. context.FlagAttributeRead(offset);
  45. bool isOutput = op.O && CanLoadOutput(offset);
  46. if (!op.P && !isOutput && TryConvertIdToIndexForVulkan(context, offset, out Operand value))
  47. {
  48. context.Copy(Register(rd), value);
  49. }
  50. else
  51. {
  52. context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, op.P));
  53. }
  54. }
  55. else
  56. {
  57. int offset = FixedFuncToUserAttribute(context.Config, op.Imm11 + index * 4, op.O);
  58. context.FlagAttributeRead(offset);
  59. bool isOutput = op.O && CanLoadOutput(offset);
  60. context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, false));
  61. }
  62. }
  63. }
  64. public static void Ast(EmitterContext context)
  65. {
  66. InstAst op = context.GetOp<InstAst>();
  67. for (int index = 0; index < (int)op.AlSize + 1; index++)
  68. {
  69. if (op.SrcB + index > RegisterConsts.RegisterZeroIndex)
  70. {
  71. break;
  72. }
  73. Register rd = new Register(op.SrcB + index, RegisterType.Gpr);
  74. if (op.Phys)
  75. {
  76. Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
  77. Operand vecIndex = context.ShiftRightU32(offset, Const(4));
  78. Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
  79. Operand invocationId = AttributeMap.HasInvocationId(context.Config.Stage, isOutput: true)
  80. ? context.Load(StorageKind.Input, IoVariable.InvocationId)
  81. : null;
  82. context.Store(StorageKind.Output, IoVariable.UserDefined, invocationId, vecIndex, elemIndex, Register(rd));
  83. }
  84. else
  85. {
  86. // TODO: Support indirect stores using Ra.
  87. int offset = op.Imm11 + index * 4;
  88. if (!context.Config.IsUsedOutputAttribute(offset))
  89. {
  90. return;
  91. }
  92. offset = FixedFuncToUserAttribute(context.Config, offset, isOutput: true);
  93. context.FlagAttributeWritten(offset);
  94. AttributeMap.GenerateAttributeStore(context, offset, op.P, Register(rd));
  95. }
  96. }
  97. }
  98. public static void Ipa(EmitterContext context)
  99. {
  100. InstIpa op = context.GetOp<InstIpa>();
  101. context.FlagAttributeRead(op.Imm10);
  102. Operand res;
  103. bool isFixedFunc = false;
  104. if (op.Idx)
  105. {
  106. Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
  107. Operand vecIndex = context.ShiftRightU32(offset, Const(4));
  108. Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
  109. res = context.Load(StorageKind.Input, IoVariable.UserDefined, null, vecIndex, elemIndex);
  110. res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3)));
  111. }
  112. else
  113. {
  114. isFixedFunc = TryFixedFuncToUserAttributeIpa(context, op.Imm10, out res);
  115. if (op.Imm10 >= AttributeConsts.UserAttributeBase && op.Imm10 < AttributeConsts.UserAttributeEnd)
  116. {
  117. int index = (op.Imm10 - AttributeConsts.UserAttributeBase) >> 4;
  118. if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
  119. {
  120. res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3)));
  121. }
  122. }
  123. else if (op.Imm10 == AttributeConsts.PositionX || op.Imm10 == AttributeConsts.PositionY)
  124. {
  125. // FragCoord X/Y must be divided by the render target scale, if resolution scaling is active,
  126. // because the shader code is not expecting scaled values.
  127. res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0)));
  128. }
  129. else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
  130. {
  131. // gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
  132. // This weird trick makes it behave.
  133. res = context.ICompareLess(context.INegate(context.IConvertS32ToFP32(res)), Const(0));
  134. }
  135. }
  136. if (op.IpaOp == IpaOp.Multiply && !isFixedFunc)
  137. {
  138. Operand srcB = GetSrcReg(context, op.SrcB);
  139. res = context.FPMultiply(res, srcB);
  140. }
  141. res = context.FPSaturate(res, op.Sat);
  142. context.Copy(GetDest(op.Dest), res);
  143. }
  144. public static void Isberd(EmitterContext context)
  145. {
  146. InstIsberd op = context.GetOp<InstIsberd>();
  147. // This instruction performs a load from ISBE (Internal Stage Buffer Entry) memory.
  148. // Here, we just propagate the offset, as the result from this instruction is usually
  149. // used with ALD to perform vertex load on geometry or tessellation shaders.
  150. // The offset is calculated as (PrimitiveIndex * VerticesPerPrimitive) + VertexIndex.
  151. // Since we hardcode PrimitiveIndex to zero, then the offset will be just VertexIndex.
  152. context.Copy(GetDest(op.Dest), GetSrcReg(context, op.SrcA));
  153. }
  154. public static void OutR(EmitterContext context)
  155. {
  156. InstOutR op = context.GetOp<InstOutR>();
  157. EmitOut(context, op.OutType.HasFlag(OutType.Emit), op.OutType.HasFlag(OutType.Cut));
  158. }
  159. public static void OutI(EmitterContext context)
  160. {
  161. InstOutI op = context.GetOp<InstOutI>();
  162. EmitOut(context, op.OutType.HasFlag(OutType.Emit), op.OutType.HasFlag(OutType.Cut));
  163. }
  164. public static void OutC(EmitterContext context)
  165. {
  166. InstOutC op = context.GetOp<InstOutC>();
  167. EmitOut(context, op.OutType.HasFlag(OutType.Emit), op.OutType.HasFlag(OutType.Cut));
  168. }
  169. private static void EmitOut(EmitterContext context, bool emit, bool cut)
  170. {
  171. if (!(emit || cut))
  172. {
  173. context.Config.GpuAccessor.Log("Invalid OUT encoding.");
  174. }
  175. if (emit)
  176. {
  177. if (context.Config.LastInVertexPipeline)
  178. {
  179. context.PrepareForVertexReturn(out var tempXLocal, out var tempYLocal, out var tempZLocal);
  180. context.EmitVertex();
  181. // Restore output position value before transformation.
  182. if (tempXLocal != null)
  183. {
  184. context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(0)), tempXLocal);
  185. }
  186. if (tempYLocal != null)
  187. {
  188. context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(1)), tempYLocal);
  189. }
  190. if (tempZLocal != null)
  191. {
  192. context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(2)), tempZLocal);
  193. }
  194. }
  195. else
  196. {
  197. context.EmitVertex();
  198. }
  199. }
  200. if (cut)
  201. {
  202. context.EndPrimitive();
  203. }
  204. }
  205. private static bool HasPrimitiveVertex(int attr)
  206. {
  207. return attr != AttributeConsts.PrimitiveId &&
  208. attr != AttributeConsts.TessCoordX &&
  209. attr != AttributeConsts.TessCoordY;
  210. }
  211. private static bool CanLoadOutput(int attr)
  212. {
  213. return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY;
  214. }
  215. private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr)
  216. {
  217. if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR)
  218. {
  219. // TODO: If two sided rendering is enabled, then this should return
  220. // FrontColor if the fragment is front facing, and back color otherwise.
  221. selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
  222. return true;
  223. }
  224. else if (attr == AttributeConsts.FogCoord)
  225. {
  226. // TODO: We likely need to emulate the fixed-function functionality for FogCoord here.
  227. selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
  228. return true;
  229. }
  230. else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0)
  231. {
  232. selectedAttr = ConstF(((attr >> 2) & 3) == 3 ? 1f : 0f);
  233. return true;
  234. }
  235. else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
  236. {
  237. selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
  238. return true;
  239. }
  240. selectedAttr = GenerateIpaLoad(context, attr);
  241. return false;
  242. }
  243. private static Operand GenerateIpaLoad(EmitterContext context, int offset)
  244. {
  245. return AttributeMap.GenerateAttributeLoad(context, null, offset, isOutput: false, isPerPatch: false);
  246. }
  247. private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
  248. {
  249. bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
  250. int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1;
  251. if (attr == AttributeConsts.Layer && config.Stage != ShaderStage.Geometry && !supportsLayerFromVertexOrTess)
  252. {
  253. attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput);
  254. config.SetLayerOutputAttribute(attr);
  255. }
  256. else if (attr == AttributeConsts.FogCoord)
  257. {
  258. attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FogCoord, fixedStartAttr, isOutput);
  259. }
  260. else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
  261. {
  262. attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr + 1, isOutput);
  263. }
  264. else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
  265. {
  266. attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 5, isOutput);
  267. }
  268. return attr;
  269. }
  270. private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput)
  271. {
  272. int index = (attr - baseAttr) >> 4;
  273. int userAttrIndex = config.GetFreeUserAttribute(isOutput, baseIndex + index);
  274. if ((uint)userAttrIndex < Constants.MaxAttributes)
  275. {
  276. attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf);
  277. if (isOutput)
  278. {
  279. config.SetOutputUserAttributeFixedFunc(userAttrIndex);
  280. }
  281. else
  282. {
  283. config.SetInputUserAttributeFixedFunc(userAttrIndex);
  284. }
  285. }
  286. else
  287. {
  288. config.GpuAccessor.Log($"No enough user attributes for fixed attribute offset 0x{attr:X}.");
  289. }
  290. return attr;
  291. }
  292. private static bool TryConvertIdToIndexForVulkan(EmitterContext context, int attr, out Operand value)
  293. {
  294. if (context.Config.Options.TargetApi == TargetApi.Vulkan)
  295. {
  296. if (attr == AttributeConsts.InstanceId)
  297. {
  298. value = context.ISubtract(
  299. context.Load(StorageKind.Input, IoVariable.InstanceIndex),
  300. context.Load(StorageKind.Input, IoVariable.BaseInstance));
  301. return true;
  302. }
  303. else if (attr == AttributeConsts.VertexId)
  304. {
  305. value = context.Load(StorageKind.Input, IoVariable.VertexIndex);
  306. return true;
  307. }
  308. }
  309. value = null;
  310. return false;
  311. }
  312. }
  313. }