EmitterContext.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. using Ryujinx.Graphics.Shader.Decoders;
  2. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Numerics;
  7. using System.Runtime.CompilerServices;
  8. using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  9. namespace Ryujinx.Graphics.Shader.Translation
  10. {
  11. class EmitterContext
  12. {
  13. public DecodedProgram Program { get; }
  14. public TranslatorContext TranslatorContext { get; }
  15. public ResourceManager ResourceManager { get; }
  16. public bool VertexAsCompute { get; }
  17. public bool IsNonMain { get; }
  18. public Block CurrBlock { get; set; }
  19. public InstOp CurrOp { get; set; }
  20. public int OperationsCount => _operations.Count;
  21. private readonly struct BrxTarget
  22. {
  23. public readonly Operand Selector;
  24. public readonly int ExpectedValue;
  25. public readonly ulong NextTargetAddress;
  26. public BrxTarget(Operand selector, int expectedValue, ulong nextTargetAddress)
  27. {
  28. Selector = selector;
  29. ExpectedValue = expectedValue;
  30. NextTargetAddress = nextTargetAddress;
  31. }
  32. }
  33. private class BlockLabel
  34. {
  35. public readonly Operand Label;
  36. public BrxTarget BrxTarget;
  37. public BlockLabel(Operand label)
  38. {
  39. Label = label;
  40. }
  41. }
  42. private readonly List<Operation> _operations;
  43. private readonly Dictionary<ulong, BlockLabel> _labels;
  44. public EmitterContext()
  45. {
  46. _operations = [];
  47. _labels = new Dictionary<ulong, BlockLabel>();
  48. }
  49. public EmitterContext(
  50. TranslatorContext translatorContext,
  51. ResourceManager resourceManager,
  52. DecodedProgram program,
  53. bool vertexAsCompute,
  54. bool isNonMain) : this()
  55. {
  56. TranslatorContext = translatorContext;
  57. ResourceManager = resourceManager;
  58. Program = program;
  59. VertexAsCompute = vertexAsCompute;
  60. IsNonMain = isNonMain;
  61. EmitStart();
  62. }
  63. private void EmitStart()
  64. {
  65. if (TranslatorContext.Options.Flags.HasFlag(TranslationFlags.VertexA))
  66. {
  67. return;
  68. }
  69. // Vulkan requires the point size to be always written on the shader if the primitive topology is points.
  70. // OpenGL requires the point size to be always written on the shader if PROGRAM_POINT_SIZE is set.
  71. if (TranslatorContext.Definitions.Stage == ShaderStage.Vertex)
  72. {
  73. this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(TranslatorContext.Definitions.PointSize));
  74. }
  75. if (VertexAsCompute)
  76. {
  77. int vertexInfoCbBinding = ResourceManager.Reservations.VertexInfoConstantBufferBinding;
  78. int countFieldIndex = TranslatorContext.Stage == ShaderStage.Vertex
  79. ? (int)VertexInfoBufferField.VertexCounts
  80. : (int)VertexInfoBufferField.GeometryCounts;
  81. Operand outputVertexOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(0));
  82. Operand vertexCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const(countFieldIndex), Const(0));
  83. Operand isVertexOob = this.ICompareGreaterOrEqualUnsigned(outputVertexOffset, vertexCount);
  84. Operand lblVertexInBounds = Label();
  85. this.BranchIfFalse(lblVertexInBounds, isVertexOob);
  86. this.Return();
  87. this.MarkLabel(lblVertexInBounds);
  88. Operand outputInstanceOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(1));
  89. Operand instanceCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(1));
  90. Operand firstVertex = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(2));
  91. Operand firstInstance = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(3));
  92. Operand ibBaseOffset = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.GeometryCounts), Const(3));
  93. Operand isInstanceOob = this.ICompareGreaterOrEqualUnsigned(outputInstanceOffset, instanceCount);
  94. Operand lblInstanceInBounds = Label();
  95. this.BranchIfFalse(lblInstanceInBounds, isInstanceOob);
  96. this.Return();
  97. this.MarkLabel(lblInstanceInBounds);
  98. if (TranslatorContext.Stage == ShaderStage.Vertex)
  99. {
  100. Operand vertexIndexVr = Local();
  101. this.TextureSample(
  102. SamplerType.TextureBuffer,
  103. TextureFlags.IntCoords,
  104. ResourceManager.Reservations.GetIndexBufferTextureSetAndBinding(),
  105. 1,
  106. [vertexIndexVr],
  107. [this.IAdd(ibBaseOffset, outputVertexOffset)]);
  108. this.Store(StorageKind.LocalMemory, ResourceManager.LocalVertexIndexVertexRateMemoryId, this.IAdd(firstVertex, vertexIndexVr));
  109. this.Store(StorageKind.LocalMemory, ResourceManager.LocalVertexIndexInstanceRateMemoryId, this.IAdd(firstInstance, outputInstanceOffset));
  110. }
  111. else if (TranslatorContext.Stage == ShaderStage.Geometry)
  112. {
  113. int inputVertices = TranslatorContext.Definitions.InputTopology.ToInputVertices();
  114. Operand baseVertex = this.IMultiply(outputVertexOffset, Const(inputVertices));
  115. for (int index = 0; index < inputVertices; index++)
  116. {
  117. Operand vertexIndex = Local();
  118. this.TextureSample(
  119. SamplerType.TextureBuffer,
  120. TextureFlags.IntCoords,
  121. ResourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(),
  122. 1,
  123. [vertexIndex],
  124. [this.IAdd(baseVertex, Const(index))]);
  125. this.Store(StorageKind.LocalMemory, ResourceManager.LocalTopologyRemapMemoryId, Const(index), vertexIndex);
  126. }
  127. this.Store(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputVertexCountMemoryId, Const(0));
  128. this.Store(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputIndexCountMemoryId, Const(0));
  129. }
  130. }
  131. }
  132. public T GetOp<T>() where T : unmanaged
  133. {
  134. Debug.Assert(Unsafe.SizeOf<T>() == sizeof(ulong));
  135. ulong op = CurrOp.RawOpCode;
  136. return Unsafe.As<ulong, T>(ref op);
  137. }
  138. public Operand Add(Instruction inst, Operand dest = null, params Operand[] sources)
  139. {
  140. Operation operation = new(inst, dest, sources);
  141. _operations.Add(operation);
  142. return dest;
  143. }
  144. public Operand Add(Instruction inst, StorageKind storageKind, Operand dest = null, params Operand[] sources)
  145. {
  146. Operation operation = new(inst, storageKind, dest, sources);
  147. _operations.Add(operation);
  148. return dest;
  149. }
  150. public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources)
  151. {
  152. Operand[] dests = [dest.Item1, dest.Item2];
  153. Operation operation = new(inst, 0, dests, sources);
  154. Add(operation);
  155. return dest;
  156. }
  157. public void Add(Operation operation)
  158. {
  159. _operations.Add(operation);
  160. }
  161. public void MarkLabel(Operand label)
  162. {
  163. Add(Instruction.MarkLabel, label);
  164. }
  165. public Operand GetLabel(ulong address)
  166. {
  167. return EnsureBlockLabel(address).Label;
  168. }
  169. public void SetBrxTarget(ulong address, Operand selector, int targetValue, ulong nextTargetAddress)
  170. {
  171. BlockLabel blockLabel = EnsureBlockLabel(address);
  172. Debug.Assert(blockLabel.BrxTarget.Selector == null);
  173. blockLabel.BrxTarget = new BrxTarget(selector, targetValue, nextTargetAddress);
  174. }
  175. public void EnterBlock(ulong address)
  176. {
  177. BlockLabel blockLabel = EnsureBlockLabel(address);
  178. MarkLabel(blockLabel.Label);
  179. BrxTarget brxTarget = blockLabel.BrxTarget;
  180. if (brxTarget.Selector != null)
  181. {
  182. this.BranchIfFalse(GetLabel(brxTarget.NextTargetAddress), this.ICompareEqual(brxTarget.Selector, Const(brxTarget.ExpectedValue)));
  183. }
  184. }
  185. private BlockLabel EnsureBlockLabel(ulong address)
  186. {
  187. if (!_labels.TryGetValue(address, out BlockLabel blockLabel))
  188. {
  189. blockLabel = new BlockLabel(Label());
  190. _labels.Add(address, blockLabel);
  191. }
  192. return blockLabel;
  193. }
  194. public void PrepareForVertexReturn()
  195. {
  196. // TODO: Support transform feedback emulation on stages other than vertex.
  197. // Those stages might produce more primitives, so it needs a way to "compact" the output after it is written.
  198. if (!TranslatorContext.GpuAccessor.QueryHostSupportsTransformFeedback() &&
  199. TranslatorContext.GpuAccessor.QueryTransformFeedbackEnabled() &&
  200. TranslatorContext.Stage == ShaderStage.Vertex)
  201. {
  202. Operand vertexCount = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.TfeVertexCount));
  203. for (int tfbIndex = 0; tfbIndex < ResourceReservations.TfeBuffersCount; tfbIndex++)
  204. {
  205. ReadOnlySpan<byte> locations = TranslatorContext.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
  206. int stride = TranslatorContext.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
  207. Operand baseOffset = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.TfeOffset), Const(tfbIndex));
  208. Operand baseVertex = this.Load(StorageKind.Input, IoVariable.BaseVertex);
  209. Operand baseInstance = this.Load(StorageKind.Input, IoVariable.BaseInstance);
  210. Operand vertexIndex = this.Load(StorageKind.Input, IoVariable.VertexIndex);
  211. Operand instanceIndex = this.Load(StorageKind.Input, IoVariable.InstanceIndex);
  212. Operand outputVertexOffset = this.ISubtract(vertexIndex, baseVertex);
  213. Operand outputInstanceOffset = this.ISubtract(instanceIndex, baseInstance);
  214. Operand outputBaseVertex = this.IMultiply(outputInstanceOffset, vertexCount);
  215. Operand vertexOffset = this.IMultiply(this.IAdd(outputBaseVertex, outputVertexOffset), Const(stride / 4));
  216. baseOffset = this.IAdd(baseOffset, vertexOffset);
  217. for (int j = 0; j < locations.Length; j++)
  218. {
  219. byte location = locations[j];
  220. if (location == 0xff)
  221. {
  222. continue;
  223. }
  224. Operand offset = this.IAdd(baseOffset, Const(j));
  225. Operand value = Instructions.AttributeMap.GenerateAttributeLoad(this, null, location * 4, isOutput: true, isPerPatch: false);
  226. int binding = ResourceManager.Reservations.GetTfeBufferStorageBufferBinding(tfbIndex);
  227. this.Store(StorageKind.StorageBuffer, binding, Const(0), offset, value);
  228. }
  229. }
  230. }
  231. if (TranslatorContext.Definitions.ViewportTransformDisable)
  232. {
  233. Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
  234. Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
  235. Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0));
  236. Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1));
  237. Operand negativeOne = ConstF(-1.0f);
  238. this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
  239. this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
  240. }
  241. if (TranslatorContext.Definitions.DepthMode && !TranslatorContext.GpuAccessor.QueryHostSupportsDepthClipControl())
  242. {
  243. Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
  244. Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
  245. Operand halfW = this.FPMultiply(w, ConstF(0.5f));
  246. this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
  247. }
  248. }
  249. public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
  250. {
  251. if (TranslatorContext.Definitions.ViewportTransformDisable)
  252. {
  253. oldXLocal = Local();
  254. this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)));
  255. oldYLocal = Local();
  256. this.Copy(oldYLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)));
  257. }
  258. else
  259. {
  260. oldXLocal = null;
  261. oldYLocal = null;
  262. }
  263. if (TranslatorContext.Definitions.DepthMode && !TranslatorContext.GpuAccessor.QueryHostSupportsDepthClipControl())
  264. {
  265. oldZLocal = Local();
  266. this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
  267. }
  268. else
  269. {
  270. oldZLocal = null;
  271. }
  272. PrepareForVertexReturn();
  273. }
  274. public bool PrepareForReturn()
  275. {
  276. if (IsNonMain)
  277. {
  278. return true;
  279. }
  280. if (TranslatorContext.Definitions.LastInVertexPipeline &&
  281. (TranslatorContext.Definitions.Stage == ShaderStage.Vertex || TranslatorContext.Definitions.Stage == ShaderStage.TessellationEvaluation) &&
  282. (TranslatorContext.Options.Flags & TranslationFlags.VertexA) == 0)
  283. {
  284. PrepareForVertexReturn();
  285. }
  286. else if (TranslatorContext.Definitions.Stage == ShaderStage.Geometry)
  287. {
  288. void WritePositionOutput(int primIndex)
  289. {
  290. Operand x = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(0));
  291. Operand y = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(1));
  292. Operand z = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(2));
  293. Operand w = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(3));
  294. this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), x);
  295. this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), y);
  296. this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), z);
  297. this.Store(StorageKind.Output, IoVariable.Position, null, Const(3), w);
  298. }
  299. void WriteUserDefinedOutput(int index, int primIndex)
  300. {
  301. Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(0));
  302. Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(1));
  303. Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(2));
  304. Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(3));
  305. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x);
  306. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y);
  307. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(2), z);
  308. this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w);
  309. }
  310. if (TranslatorContext.Definitions.GpPassthrough && !TranslatorContext.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  311. {
  312. int inputStart, inputEnd, inputStep;
  313. InputTopology topology = TranslatorContext.Definitions.InputTopology;
  314. if (topology == InputTopology.LinesAdjacency)
  315. {
  316. inputStart = 1;
  317. inputEnd = 3;
  318. inputStep = 1;
  319. }
  320. else if (topology == InputTopology.TrianglesAdjacency)
  321. {
  322. inputStart = 0;
  323. inputEnd = 6;
  324. inputStep = 2;
  325. }
  326. else
  327. {
  328. inputStart = 0;
  329. inputEnd = topology.ToInputVerticesNoAdjacency();
  330. inputStep = 1;
  331. }
  332. for (int primIndex = inputStart; primIndex < inputEnd; primIndex += inputStep)
  333. {
  334. WritePositionOutput(primIndex);
  335. int passthroughAttributes = TranslatorContext.AttributeUsage.PassthroughAttributes;
  336. while (passthroughAttributes != 0)
  337. {
  338. int index = BitOperations.TrailingZeroCount(passthroughAttributes);
  339. WriteUserDefinedOutput(index, primIndex);
  340. passthroughAttributes &= ~(1 << index);
  341. }
  342. this.EmitVertex();
  343. }
  344. this.EndPrimitive();
  345. }
  346. }
  347. else if (TranslatorContext.Definitions.Stage == ShaderStage.Fragment)
  348. {
  349. GenerateAlphaToCoverageDitherDiscard();
  350. bool supportsBgra = TranslatorContext.GpuAccessor.QueryHostSupportsBgraFormat();
  351. if (TranslatorContext.Definitions.OmapDepth)
  352. {
  353. Operand src = Register(TranslatorContext.GetDepthRegister(), RegisterType.Gpr);
  354. this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src);
  355. }
  356. AlphaTestOp alphaTestOp = TranslatorContext.Definitions.AlphaTestCompare;
  357. if (alphaTestOp != AlphaTestOp.Always)
  358. {
  359. if (alphaTestOp == AlphaTestOp.Never)
  360. {
  361. this.Discard();
  362. }
  363. else if ((TranslatorContext.Definitions.OmapTargets & 8) != 0)
  364. {
  365. Instruction comparator = alphaTestOp switch
  366. {
  367. AlphaTestOp.Equal => Instruction.CompareEqual,
  368. AlphaTestOp.Greater => Instruction.CompareGreater,
  369. AlphaTestOp.GreaterOrEqual => Instruction.CompareGreaterOrEqual,
  370. AlphaTestOp.Less => Instruction.CompareLess,
  371. AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual,
  372. AlphaTestOp.NotEqual => Instruction.CompareNotEqual,
  373. _ => 0,
  374. };
  375. Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\".");
  376. Operand alpha = Register(3, RegisterType.Gpr);
  377. Operand alphaRef = ConstF(TranslatorContext.Definitions.AlphaTestReference);
  378. Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef);
  379. Operand alphaPassLabel = Label();
  380. this.BranchIfTrue(alphaPassLabel, alphaPass);
  381. this.Discard();
  382. this.MarkLabel(alphaPassLabel);
  383. }
  384. }
  385. // We don't need to output anything if alpha test always fails.
  386. if (alphaTestOp == AlphaTestOp.Never)
  387. {
  388. return false;
  389. }
  390. int regIndexBase = 0;
  391. for (int rtIndex = 0; rtIndex < 8; rtIndex++)
  392. {
  393. for (int component = 0; component < 4; component++)
  394. {
  395. bool componentEnabled = (TranslatorContext.Definitions.OmapTargets & (1 << (rtIndex * 4 + component))) != 0;
  396. if (!componentEnabled)
  397. {
  398. continue;
  399. }
  400. Operand src = Register(regIndexBase + component, RegisterType.Gpr);
  401. // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
  402. if (!supportsBgra && (component == 0 || component == 2))
  403. {
  404. Operand isBgra = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.FragmentIsBgra), Const(rtIndex));
  405. Operand lblIsBgra = Label();
  406. Operand lblEnd = Label();
  407. this.BranchIfTrue(lblIsBgra, isBgra);
  408. this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
  409. this.Branch(lblEnd);
  410. MarkLabel(lblIsBgra);
  411. this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(2 - component), src);
  412. MarkLabel(lblEnd);
  413. }
  414. else
  415. {
  416. this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
  417. }
  418. }
  419. bool targetEnabled = (TranslatorContext.Definitions.OmapTargets & (0xf << (rtIndex * 4))) != 0;
  420. if (targetEnabled)
  421. {
  422. regIndexBase += 4;
  423. }
  424. }
  425. }
  426. if (VertexAsCompute)
  427. {
  428. if (TranslatorContext.Stage == ShaderStage.Vertex)
  429. {
  430. int vertexInfoCbBinding = ResourceManager.Reservations.VertexInfoConstantBufferBinding;
  431. int vertexOutputSbBinding = ResourceManager.Reservations.VertexOutputStorageBufferBinding;
  432. int stride = ResourceManager.Reservations.OutputSizePerInvocation;
  433. Operand vertexCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(0));
  434. Operand outputVertexOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(0));
  435. Operand outputInstanceOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(1));
  436. Operand outputBaseVertex = this.IMultiply(outputInstanceOffset, vertexCount);
  437. Operand baseOffset = this.IMultiply(this.IAdd(outputBaseVertex, outputVertexOffset), Const(stride));
  438. for (int offset = 0; offset < stride; offset++)
  439. {
  440. Operand vertexOffset = this.IAdd(baseOffset, Const(offset));
  441. Operand value = this.Load(StorageKind.LocalMemory, ResourceManager.LocalVertexDataMemoryId, Const(offset));
  442. this.Store(StorageKind.StorageBuffer, vertexOutputSbBinding, Const(0), vertexOffset, value);
  443. }
  444. }
  445. else if (TranslatorContext.Stage == ShaderStage.Geometry)
  446. {
  447. Operand lblLoopHead = Label();
  448. Operand lblExit = Label();
  449. this.MarkLabel(lblLoopHead);
  450. Operand writtenIndices = this.Load(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputIndexCountMemoryId);
  451. int maxIndicesPerPrimitiveInvocation = TranslatorContext.Definitions.GetGeometryOutputIndexBufferStridePerInstance();
  452. int maxIndicesPerPrimitive = maxIndicesPerPrimitiveInvocation * TranslatorContext.Definitions.ThreadsPerInputPrimitive;
  453. this.BranchIfTrue(lblExit, this.ICompareGreaterOrEqualUnsigned(writtenIndices, Const(maxIndicesPerPrimitiveInvocation)));
  454. int vertexInfoCbBinding = ResourceManager.Reservations.VertexInfoConstantBufferBinding;
  455. Operand primitiveIndex = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(0));
  456. Operand instanceIndex = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(1));
  457. Operand invocationId = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(2));
  458. Operand vertexCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(0));
  459. Operand primitiveId = this.IAdd(this.IMultiply(instanceIndex, vertexCount), primitiveIndex);
  460. Operand ibOffset = this.IMultiply(primitiveId, Const(maxIndicesPerPrimitive));
  461. ibOffset = this.IAdd(ibOffset, this.IMultiply(invocationId, Const(maxIndicesPerPrimitiveInvocation)));
  462. ibOffset = this.IAdd(ibOffset, writtenIndices);
  463. this.Store(StorageKind.StorageBuffer, ResourceManager.Reservations.GeometryIndexOutputStorageBufferBinding, Const(0), ibOffset, Const(-1));
  464. this.Store(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputIndexCountMemoryId, this.IAdd(writtenIndices, Const(1)));
  465. this.Branch(lblLoopHead);
  466. this.MarkLabel(lblExit);
  467. }
  468. }
  469. return true;
  470. }
  471. private void GenerateAlphaToCoverageDitherDiscard()
  472. {
  473. // If the feature is disabled, or alpha is not written, then we're done.
  474. if (!TranslatorContext.Definitions.AlphaToCoverageDitherEnable || (TranslatorContext.Definitions.OmapTargets & 8) == 0)
  475. {
  476. return;
  477. }
  478. // 11 11 11 10 10 10 10 00
  479. // 11 01 01 01 01 00 00 00
  480. Operand ditherMask = Const(unchecked((int)0xfbb99110u));
  481. Operand fragCoordX = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0));
  482. Operand fragCoordY = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1));
  483. Operand x = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordX), Const(1));
  484. Operand y = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordY), Const(1));
  485. Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
  486. Operand alpha = Register(3, RegisterType.Gpr);
  487. Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8));
  488. Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7));
  489. Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy);
  490. Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1));
  491. Operand a2cDitherEndLabel = Label();
  492. this.BranchIfTrue(a2cDitherEndLabel, opaque);
  493. this.Discard();
  494. this.MarkLabel(a2cDitherEndLabel);
  495. }
  496. public Operation[] GetOperations()
  497. {
  498. return _operations.ToArray();
  499. }
  500. }
  501. }