EmitterContext.cs 20 KB

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