EmitterContext.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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(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(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(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.QueryHostSupportsTransformFeedback() && Config.GpuAccessor.QueryTransformFeedbackEnabled())
  195. {
  196. Operand vertexCount = this.Load(StorageKind.StorageBuffer, Constants.TfeInfoBinding, Const(1));
  197. for (int tfbIndex = 0; tfbIndex < Constants.TfeBuffersCount; tfbIndex++)
  198. {
  199. var locations = Config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
  200. var stride = Config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
  201. Operand baseOffset = this.Load(StorageKind.StorageBuffer, Constants.TfeInfoBinding, Const(0), Const(tfbIndex));
  202. Operand baseVertex = this.Load(StorageKind.Input, IoVariable.BaseVertex);
  203. Operand baseInstance = this.Load(StorageKind.Input, IoVariable.BaseInstance);
  204. Operand vertexIndex = this.Load(StorageKind.Input, IoVariable.VertexIndex);
  205. Operand instanceIndex = this.Load(StorageKind.Input, IoVariable.InstanceIndex);
  206. Operand outputVertexOffset = this.ISubtract(vertexIndex, baseVertex);
  207. Operand outputInstanceOffset = this.ISubtract(instanceIndex, baseInstance);
  208. Operand outputBaseVertex = this.IMultiply(outputInstanceOffset, vertexCount);
  209. Operand vertexOffset = this.IMultiply(this.IAdd(outputBaseVertex, outputVertexOffset), Const(stride / 4));
  210. baseOffset = this.IAdd(baseOffset, vertexOffset);
  211. for (int j = 0; j < locations.Length; j++)
  212. {
  213. byte location = locations[j];
  214. if (location == 0xff)
  215. {
  216. continue;
  217. }
  218. Operand offset = this.IAdd(baseOffset, Const(j));
  219. Operand value = Instructions.AttributeMap.GenerateAttributeLoad(this, null, location * 4, isOutput: true, isPerPatch: false);
  220. this.Store(StorageKind.StorageBuffer, Constants.TfeBufferBaseBinding + tfbIndex, Const(0), offset, value);
  221. }
  222. }
  223. }
  224. if (Config.GpuAccessor.QueryViewportTransformDisable())
  225. {
  226. Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
  227. Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
  228. Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0));
  229. Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1));
  230. Operand negativeOne = ConstF(-1.0f);
  231. this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
  232. this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
  233. }
  234. if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl())
  235. {
  236. Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
  237. Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
  238. Operand halfW = this.FPMultiply(w, ConstF(0.5f));
  239. this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
  240. }
  241. if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute)
  242. {
  243. Config.SetUsedFeature(FeatureFlags.RtLayer);
  244. int attrVecIndex = Config.GpLayerInputAttribute >> 2;
  245. int attrComponentIndex = Config.GpLayerInputAttribute & 3;
  246. Operand layer = this.Load(StorageKind.Output, IoVariable.UserDefined, null, Const(attrVecIndex), Const(attrComponentIndex));
  247. this.Store(StorageKind.Output, IoVariable.Layer, null, layer);
  248. }
  249. }
  250. public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
  251. {
  252. if (Config.GpuAccessor.QueryViewportTransformDisable())
  253. {
  254. oldXLocal = Local();
  255. this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)));
  256. oldYLocal = Local();
  257. this.Copy(oldYLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)));
  258. }
  259. else
  260. {
  261. oldXLocal = null;
  262. oldYLocal = null;
  263. }
  264. if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl())
  265. {
  266. oldZLocal = Local();
  267. this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
  268. }
  269. else
  270. {
  271. oldZLocal = null;
  272. }
  273. PrepareForVertexReturn();
  274. }
  275. public void PrepareForReturn()
  276. {
  277. if (IsNonMain)
  278. {
  279. return;
  280. }
  281. if (Config.LastInVertexPipeline &&
  282. (Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) &&
  283. (Config.Options.Flags & TranslationFlags.VertexA) == 0)
  284. {
  285. PrepareForVertexReturn();
  286. }
  287. else if (Config.Stage == ShaderStage.Geometry)
  288. {
  289. void WritePositionOutput(int primIndex)
  290. {
  291. Operand x = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(0));
  292. Operand y = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(1));
  293. Operand z = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(2));
  294. Operand w = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(3));
  295. this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), x);
  296. this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), y);
  297. this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), z);
  298. this.Store(StorageKind.Output, IoVariable.Position, null, Const(3), w);
  299. }
  300. void WriteUserDefinedOutput(int index, int primIndex)
  301. {
  302. Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(0));
  303. Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(1));
  304. Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(2));
  305. Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(3));
  306. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x);
  307. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y);
  308. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(2), z);
  309. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w);
  310. }
  311. if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  312. {
  313. int inputVertices = Config.GpuAccessor.QueryPrimitiveTopology().ToInputVertices();
  314. for (int primIndex = 0; primIndex < inputVertices; primIndex++)
  315. {
  316. WritePositionOutput(primIndex);
  317. int passthroughAttributes = Config.PassthroughAttributes;
  318. while (passthroughAttributes != 0)
  319. {
  320. int index = BitOperations.TrailingZeroCount(passthroughAttributes);
  321. WriteUserDefinedOutput(index, primIndex);
  322. Config.SetOutputUserAttribute(index);
  323. passthroughAttributes &= ~(1 << index);
  324. }
  325. this.EmitVertex();
  326. }
  327. this.EndPrimitive();
  328. }
  329. }
  330. else if (Config.Stage == ShaderStage.Fragment)
  331. {
  332. GenerateAlphaToCoverageDitherDiscard();
  333. bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat();
  334. if (Config.OmapDepth)
  335. {
  336. Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr);
  337. this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src);
  338. }
  339. AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
  340. if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0)
  341. {
  342. if (alphaTestOp == AlphaTestOp.Never)
  343. {
  344. this.Discard();
  345. }
  346. else
  347. {
  348. Instruction comparator = alphaTestOp switch
  349. {
  350. AlphaTestOp.Equal => Instruction.CompareEqual,
  351. AlphaTestOp.Greater => Instruction.CompareGreater,
  352. AlphaTestOp.GreaterOrEqual => Instruction.CompareGreaterOrEqual,
  353. AlphaTestOp.Less => Instruction.CompareLess,
  354. AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual,
  355. AlphaTestOp.NotEqual => Instruction.CompareNotEqual,
  356. _ => 0,
  357. };
  358. Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\".");
  359. Operand alpha = Register(3, RegisterType.Gpr);
  360. Operand alphaRef = ConstF(Config.GpuAccessor.QueryAlphaTestReference());
  361. Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef);
  362. Operand alphaPassLabel = Label();
  363. this.BranchIfTrue(alphaPassLabel, alphaPass);
  364. this.Discard();
  365. this.MarkLabel(alphaPassLabel);
  366. }
  367. }
  368. int regIndexBase = 0;
  369. for (int rtIndex = 0; rtIndex < 8; rtIndex++)
  370. {
  371. for (int component = 0; component < 4; component++)
  372. {
  373. bool componentEnabled = (Config.OmapTargets & (1 << (rtIndex * 4 + component))) != 0;
  374. if (!componentEnabled)
  375. {
  376. continue;
  377. }
  378. Operand src = Register(regIndexBase + component, RegisterType.Gpr);
  379. // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
  380. if (!supportsBgra && (component == 0 || component == 2))
  381. {
  382. Operand isBgra = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.FragmentIsBgra), Const(rtIndex));
  383. Operand lblIsBgra = Label();
  384. Operand lblEnd = Label();
  385. this.BranchIfTrue(lblIsBgra, isBgra);
  386. this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
  387. this.Branch(lblEnd);
  388. MarkLabel(lblIsBgra);
  389. this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(2 - component), src);
  390. MarkLabel(lblEnd);
  391. }
  392. else
  393. {
  394. this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
  395. }
  396. }
  397. bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0;
  398. if (targetEnabled)
  399. {
  400. Config.SetOutputUserAttribute(rtIndex);
  401. regIndexBase += 4;
  402. }
  403. }
  404. }
  405. }
  406. private void GenerateAlphaToCoverageDitherDiscard()
  407. {
  408. // If the feature is disabled, or alpha is not written, then we're done.
  409. if (!Config.GpuAccessor.QueryAlphaToCoverageDitherEnable() || (Config.OmapTargets & 8) == 0)
  410. {
  411. return;
  412. }
  413. // 11 11 11 10 10 10 10 00
  414. // 11 01 01 01 01 00 00 00
  415. Operand ditherMask = Const(unchecked((int)0xfbb99110u));
  416. Operand fragCoordX = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0));
  417. Operand fragCoordY = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1));
  418. Operand x = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordX), Const(1));
  419. Operand y = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordY), Const(1));
  420. Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
  421. Operand alpha = Register(3, RegisterType.Gpr);
  422. Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8));
  423. Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7));
  424. Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy);
  425. Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1));
  426. Operand a2cDitherEndLabel = Label();
  427. this.BranchIfTrue(a2cDitherEndLabel, opaque);
  428. this.Discard();
  429. this.MarkLabel(a2cDitherEndLabel);
  430. }
  431. public Operation[] GetOperations()
  432. {
  433. return _operations.ToArray();
  434. }
  435. }
  436. }