EmitterContext.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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(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[] dests,
  90. params Operand[] sources)
  91. {
  92. return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dests, 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[] dests,
  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, dests, 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. if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute)
  203. {
  204. Config.SetUsedFeature(FeatureFlags.RtLayer);
  205. this.Copy(Attribute(AttributeConsts.Layer), Attribute(Config.GpLayerInputAttribute | AttributeConsts.LoadOutputMask));
  206. }
  207. }
  208. public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
  209. {
  210. if (Config.GpuAccessor.QueryViewportTransformDisable())
  211. {
  212. oldXLocal = Local();
  213. this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask));
  214. oldYLocal = Local();
  215. this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask));
  216. }
  217. else
  218. {
  219. oldXLocal = null;
  220. oldYLocal = null;
  221. }
  222. if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
  223. {
  224. oldZLocal = Local();
  225. this.Copy(oldZLocal, Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask));
  226. }
  227. else
  228. {
  229. oldZLocal = null;
  230. }
  231. PrepareForVertexReturn();
  232. }
  233. public void PrepareForReturn()
  234. {
  235. if (IsNonMain)
  236. {
  237. return;
  238. }
  239. if (Config.LastInVertexPipeline &&
  240. (Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) &&
  241. (Config.Options.Flags & TranslationFlags.VertexA) == 0)
  242. {
  243. PrepareForVertexReturn();
  244. }
  245. else if (Config.Stage == ShaderStage.Geometry)
  246. {
  247. void WriteOutput(int index, int primIndex)
  248. {
  249. Operand x = this.LoadAttribute(Const(index), Const(0), Const(primIndex));
  250. Operand y = this.LoadAttribute(Const(index + 4), Const(0), Const(primIndex));
  251. Operand z = this.LoadAttribute(Const(index + 8), Const(0), Const(primIndex));
  252. Operand w = this.LoadAttribute(Const(index + 12), Const(0), Const(primIndex));
  253. this.Copy(Attribute(index), x);
  254. this.Copy(Attribute(index + 4), y);
  255. this.Copy(Attribute(index + 8), z);
  256. this.Copy(Attribute(index + 12), w);
  257. }
  258. if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  259. {
  260. int inputVertices = Config.GpuAccessor.QueryPrimitiveTopology().ToInputVertices();
  261. for (int primIndex = 0; primIndex < inputVertices; primIndex++)
  262. {
  263. WriteOutput(AttributeConsts.PositionX, primIndex);
  264. int passthroughAttributes = Config.PassthroughAttributes;
  265. while (passthroughAttributes != 0)
  266. {
  267. int index = BitOperations.TrailingZeroCount(passthroughAttributes);
  268. WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
  269. Config.SetOutputUserAttribute(index);
  270. passthroughAttributes &= ~(1 << index);
  271. }
  272. this.EmitVertex();
  273. }
  274. this.EndPrimitive();
  275. }
  276. }
  277. else if (Config.Stage == ShaderStage.Fragment)
  278. {
  279. GenerateAlphaToCoverageDitherDiscard();
  280. bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat();
  281. if (Config.OmapDepth)
  282. {
  283. Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
  284. Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr);
  285. this.Copy(dest, src);
  286. }
  287. AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
  288. if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0)
  289. {
  290. if (alphaTestOp == AlphaTestOp.Never)
  291. {
  292. this.Discard();
  293. }
  294. else
  295. {
  296. Instruction comparator = alphaTestOp switch
  297. {
  298. AlphaTestOp.Equal => Instruction.CompareEqual,
  299. AlphaTestOp.Greater => Instruction.CompareGreater,
  300. AlphaTestOp.GreaterOrEqual => Instruction.CompareGreaterOrEqual,
  301. AlphaTestOp.Less => Instruction.CompareLess,
  302. AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual,
  303. AlphaTestOp.NotEqual => Instruction.CompareNotEqual,
  304. _ => 0
  305. };
  306. Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\".");
  307. Operand alpha = Register(3, RegisterType.Gpr);
  308. Operand alphaRef = ConstF(Config.GpuAccessor.QueryAlphaTestReference());
  309. Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef);
  310. Operand alphaPassLabel = Label();
  311. this.BranchIfTrue(alphaPassLabel, alphaPass);
  312. this.Discard();
  313. this.MarkLabel(alphaPassLabel);
  314. }
  315. }
  316. int regIndexBase = 0;
  317. for (int rtIndex = 0; rtIndex < 8; rtIndex++)
  318. {
  319. for (int component = 0; component < 4; component++)
  320. {
  321. bool componentEnabled = (Config.OmapTargets & (1 << (rtIndex * 4 + component))) != 0;
  322. if (!componentEnabled)
  323. {
  324. continue;
  325. }
  326. int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16;
  327. Operand src = Register(regIndexBase + component, RegisterType.Gpr);
  328. // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
  329. if (!supportsBgra && (component == 0 || component == 2))
  330. {
  331. Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4);
  332. Operand lblIsBgra = Label();
  333. Operand lblEnd = Label();
  334. this.BranchIfTrue(lblIsBgra, isBgra);
  335. this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
  336. this.Branch(lblEnd);
  337. MarkLabel(lblIsBgra);
  338. this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src);
  339. MarkLabel(lblEnd);
  340. }
  341. else
  342. {
  343. this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
  344. }
  345. }
  346. bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0;
  347. if (targetEnabled)
  348. {
  349. Config.SetOutputUserAttribute(rtIndex);
  350. regIndexBase += 4;
  351. }
  352. }
  353. }
  354. }
  355. private void GenerateAlphaToCoverageDitherDiscard()
  356. {
  357. // If the feature is disabled, or alpha is not written, then we're done.
  358. if (!Config.GpuAccessor.QueryAlphaToCoverageDitherEnable() || (Config.OmapTargets & 8) == 0)
  359. {
  360. return;
  361. }
  362. // 11 11 11 10 10 10 10 00
  363. // 11 01 01 01 01 00 00 00
  364. Operand ditherMask = Const(unchecked((int)0xfbb99110u));
  365. Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1));
  366. Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1));
  367. Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
  368. Operand alpha = Register(3, RegisterType.Gpr);
  369. Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8));
  370. Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7));
  371. Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy);
  372. Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1));
  373. Operand a2cDitherEndLabel = Label();
  374. this.BranchIfTrue(a2cDitherEndLabel, opaque);
  375. this.Discard();
  376. this.MarkLabel(a2cDitherEndLabel);
  377. }
  378. public Operation[] GetOperations()
  379. {
  380. return _operations.ToArray();
  381. }
  382. }
  383. }