EmitterContext.cs 17 KB

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