EmitterContext.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. using Ryujinx.Graphics.Shader.Decoders;
  2. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Numerics;
  6. using System.Runtime.CompilerServices;
  7. using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  8. namespace Ryujinx.Graphics.Shader.Translation
  9. {
  10. class EmitterContext
  11. {
  12. public DecodedProgram Program { get; }
  13. public ShaderConfig Config { get; }
  14. public bool IsNonMain { get; }
  15. public Block CurrBlock { get; set; }
  16. public InstOp CurrOp { get; set; }
  17. public int OperationsCount => _operations.Count;
  18. private readonly List<Operation> _operations;
  19. private readonly Dictionary<ulong, Operand> _labels;
  20. public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
  21. {
  22. Program = program;
  23. Config = config;
  24. IsNonMain = isNonMain;
  25. _operations = new List<Operation>();
  26. _labels = new Dictionary<ulong, Operand>();
  27. EmitStart();
  28. }
  29. private void EmitStart()
  30. {
  31. if (Config.Stage == ShaderStage.Vertex &&
  32. Config.Options.TargetApi == TargetApi.Vulkan &&
  33. (Config.Options.Flags & TranslationFlags.VertexA) == 0)
  34. {
  35. // Vulkan requires the point size to be always written on the shader if the primitive topology is points.
  36. this.Copy(Attribute(AttributeConsts.PointSize), ConstF(Config.GpuAccessor.QueryPointSize()));
  37. }
  38. }
  39. public T GetOp<T>() where T : unmanaged
  40. {
  41. Debug.Assert(Unsafe.SizeOf<T>() == sizeof(ulong));
  42. ulong op = CurrOp.RawOpCode;
  43. return Unsafe.As<ulong, T>(ref op);
  44. }
  45. public Operand Add(Instruction inst, Operand dest = null, params Operand[] sources)
  46. {
  47. Operation operation = new Operation(inst, dest, sources);
  48. _operations.Add(operation);
  49. return dest;
  50. }
  51. public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources)
  52. {
  53. Operand[] dests = new[] { dest.Item1, dest.Item2 };
  54. Operation operation = new Operation(inst, 0, dests, sources);
  55. Add(operation);
  56. return dest;
  57. }
  58. public void Add(Operation operation)
  59. {
  60. _operations.Add(operation);
  61. }
  62. public TextureOperation CreateTextureOperation(
  63. Instruction inst,
  64. SamplerType type,
  65. TextureFlags flags,
  66. int handle,
  67. int compIndex,
  68. Operand dest,
  69. params Operand[] sources)
  70. {
  71. return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dest, sources);
  72. }
  73. public TextureOperation CreateTextureOperation(
  74. Instruction inst,
  75. SamplerType type,
  76. TextureFormat format,
  77. TextureFlags flags,
  78. int handle,
  79. int compIndex,
  80. Operand dest,
  81. params Operand[] sources)
  82. {
  83. if (!flags.HasFlag(TextureFlags.Bindless))
  84. {
  85. Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle);
  86. }
  87. return new TextureOperation(inst, type, format, flags, handle, compIndex, dest, sources);
  88. }
  89. public void FlagAttributeRead(int attribute)
  90. {
  91. if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId)
  92. {
  93. Config.SetUsedFeature(FeatureFlags.InstanceId);
  94. }
  95. else if (Config.Stage == ShaderStage.Fragment)
  96. {
  97. switch (attribute)
  98. {
  99. case AttributeConsts.PositionX:
  100. case AttributeConsts.PositionY:
  101. Config.SetUsedFeature(FeatureFlags.FragCoordXY);
  102. break;
  103. }
  104. }
  105. }
  106. public void FlagAttributeWritten(int attribute)
  107. {
  108. if (Config.Stage == ShaderStage.Vertex)
  109. {
  110. switch (attribute)
  111. {
  112. case AttributeConsts.ClipDistance0:
  113. case AttributeConsts.ClipDistance1:
  114. case AttributeConsts.ClipDistance2:
  115. case AttributeConsts.ClipDistance3:
  116. case AttributeConsts.ClipDistance4:
  117. case AttributeConsts.ClipDistance5:
  118. case AttributeConsts.ClipDistance6:
  119. case AttributeConsts.ClipDistance7:
  120. Config.SetClipDistanceWritten((attribute - AttributeConsts.ClipDistance0) / 4);
  121. break;
  122. }
  123. }
  124. if (Config.Stage != ShaderStage.Fragment && attribute == AttributeConsts.Layer)
  125. {
  126. Config.SetUsedFeature(FeatureFlags.RtLayer);
  127. }
  128. }
  129. public void MarkLabel(Operand label)
  130. {
  131. Add(Instruction.MarkLabel, label);
  132. }
  133. public Operand GetLabel(ulong address)
  134. {
  135. if (!_labels.TryGetValue(address, out Operand label))
  136. {
  137. label = Label();
  138. _labels.Add(address, label);
  139. }
  140. return label;
  141. }
  142. public void PrepareForVertexReturn()
  143. {
  144. if (Config.GpuAccessor.QueryViewportTransformDisable())
  145. {
  146. Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask);
  147. Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask);
  148. Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX);
  149. Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY);
  150. Operand negativeOne = ConstF(-1.0f);
  151. this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
  152. this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
  153. }
  154. if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
  155. {
  156. Operand z = Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask);
  157. Operand w = Attribute(AttributeConsts.PositionW | AttributeConsts.LoadOutputMask);
  158. Operand halfW = this.FPMultiply(w, ConstF(0.5f));
  159. this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
  160. }
  161. }
  162. public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
  163. {
  164. if (Config.GpuAccessor.QueryViewportTransformDisable())
  165. {
  166. oldXLocal = Local();
  167. this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask));
  168. oldYLocal = Local();
  169. this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask));
  170. }
  171. else
  172. {
  173. oldXLocal = null;
  174. oldYLocal = null;
  175. }
  176. if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
  177. {
  178. oldZLocal = Local();
  179. this.Copy(oldZLocal, Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask));
  180. }
  181. else
  182. {
  183. oldZLocal = null;
  184. }
  185. PrepareForVertexReturn();
  186. }
  187. public void PrepareForReturn()
  188. {
  189. if (IsNonMain)
  190. {
  191. return;
  192. }
  193. if (Config.LastInVertexPipeline &&
  194. (Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) &&
  195. (Config.Options.Flags & TranslationFlags.VertexA) == 0)
  196. {
  197. PrepareForVertexReturn();
  198. }
  199. else if (Config.Stage == ShaderStage.Geometry)
  200. {
  201. void WriteOutput(int index, int primIndex)
  202. {
  203. Operand x = this.LoadAttribute(Const(index), Const(0), Const(primIndex));
  204. Operand y = this.LoadAttribute(Const(index + 4), Const(0), Const(primIndex));
  205. Operand z = this.LoadAttribute(Const(index + 8), Const(0), Const(primIndex));
  206. Operand w = this.LoadAttribute(Const(index + 12), Const(0), Const(primIndex));
  207. this.Copy(Attribute(index), x);
  208. this.Copy(Attribute(index + 4), y);
  209. this.Copy(Attribute(index + 8), z);
  210. this.Copy(Attribute(index + 12), w);
  211. }
  212. if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  213. {
  214. int inputVertices = Config.GpuAccessor.QueryPrimitiveTopology().ToInputVertices();
  215. for (int primIndex = 0; primIndex < inputVertices; primIndex++)
  216. {
  217. WriteOutput(AttributeConsts.PositionX, primIndex);
  218. int passthroughAttributes = Config.PassthroughAttributes;
  219. while (passthroughAttributes != 0)
  220. {
  221. int index = BitOperations.TrailingZeroCount(passthroughAttributes);
  222. WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
  223. Config.SetOutputUserAttribute(index, perPatch: false);
  224. passthroughAttributes &= ~(1 << index);
  225. }
  226. this.EmitVertex();
  227. }
  228. this.EndPrimitive();
  229. }
  230. }
  231. else if (Config.Stage == ShaderStage.Fragment)
  232. {
  233. GenerateAlphaToCoverageDitherDiscard();
  234. bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat();
  235. if (Config.OmapDepth)
  236. {
  237. Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
  238. Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr);
  239. this.Copy(dest, src);
  240. }
  241. AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
  242. if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0)
  243. {
  244. if (alphaTestOp == AlphaTestOp.Never)
  245. {
  246. this.Discard();
  247. }
  248. else
  249. {
  250. Instruction comparator = alphaTestOp switch
  251. {
  252. AlphaTestOp.Equal => Instruction.CompareEqual,
  253. AlphaTestOp.Greater => Instruction.CompareGreater,
  254. AlphaTestOp.GreaterOrEqual => Instruction.CompareGreaterOrEqual,
  255. AlphaTestOp.Less => Instruction.CompareLess,
  256. AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual,
  257. AlphaTestOp.NotEqual => Instruction.CompareNotEqual,
  258. _ => 0
  259. };
  260. Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\".");
  261. Operand alpha = Register(3, RegisterType.Gpr);
  262. Operand alphaRef = ConstF(Config.GpuAccessor.QueryAlphaTestReference());
  263. Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef);
  264. Operand alphaPassLabel = Label();
  265. this.BranchIfTrue(alphaPassLabel, alphaPass);
  266. this.Discard();
  267. this.MarkLabel(alphaPassLabel);
  268. }
  269. }
  270. int regIndexBase = 0;
  271. for (int rtIndex = 0; rtIndex < 8; rtIndex++)
  272. {
  273. for (int component = 0; component < 4; component++)
  274. {
  275. bool componentEnabled = (Config.OmapTargets & (1 << (rtIndex * 4 + component))) != 0;
  276. if (!componentEnabled)
  277. {
  278. continue;
  279. }
  280. int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16;
  281. Operand src = Register(regIndexBase + component, RegisterType.Gpr);
  282. // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
  283. if (!supportsBgra && (component == 0 || component == 2))
  284. {
  285. Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4);
  286. Operand lblIsBgra = Label();
  287. Operand lblEnd = Label();
  288. this.BranchIfTrue(lblIsBgra, isBgra);
  289. this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
  290. this.Branch(lblEnd);
  291. MarkLabel(lblIsBgra);
  292. this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src);
  293. MarkLabel(lblEnd);
  294. }
  295. else
  296. {
  297. this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
  298. }
  299. }
  300. bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0;
  301. if (targetEnabled)
  302. {
  303. Config.SetOutputUserAttribute(rtIndex, perPatch: false);
  304. regIndexBase += 4;
  305. }
  306. }
  307. }
  308. }
  309. private void GenerateAlphaToCoverageDitherDiscard()
  310. {
  311. // If the feature is disabled, or alpha is not written, then we're done.
  312. if (!Config.GpuAccessor.QueryAlphaToCoverageDitherEnable() || (Config.OmapTargets & 8) == 0)
  313. {
  314. return;
  315. }
  316. // 11 11 11 10 10 10 10 00
  317. // 11 01 01 01 01 00 00 00
  318. Operand ditherMask = Const(unchecked((int)0xfbb99110u));
  319. Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1));
  320. Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1));
  321. Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
  322. Operand alpha = Register(3, RegisterType.Gpr);
  323. Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8));
  324. Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7));
  325. Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy);
  326. Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1));
  327. Operand a2cDitherEndLabel = Label();
  328. this.BranchIfTrue(a2cDitherEndLabel, opaque);
  329. this.Discard();
  330. this.MarkLabel(a2cDitherEndLabel);
  331. }
  332. public Operation[] GetOperations()
  333. {
  334. return _operations.ToArray();
  335. }
  336. }
  337. }