GeometryToCompute.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2. using Ryujinx.Graphics.Shader.Translation.Optimizations;
  3. using System.Collections.Generic;
  4. using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  5. namespace Ryujinx.Graphics.Shader.Translation.Transforms
  6. {
  7. class GeometryToCompute : ITransformPass
  8. {
  9. public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures)
  10. {
  11. return usedFeatures.HasFlag(FeatureFlags.VtgAsCompute);
  12. }
  13. public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node)
  14. {
  15. if (context.Definitions.Stage != ShaderStage.Geometry)
  16. {
  17. return node;
  18. }
  19. Operation operation = (Operation)node.Value;
  20. LinkedListNode<INode> newNode = node;
  21. switch (operation.Inst)
  22. {
  23. case Instruction.EmitVertex:
  24. newNode = GenerateEmitVertex(context.Definitions, context.ResourceManager, node);
  25. break;
  26. case Instruction.EndPrimitive:
  27. newNode = GenerateEndPrimitive(context.Definitions, context.ResourceManager, node);
  28. break;
  29. case Instruction.Load:
  30. if (operation.StorageKind == StorageKind.Input)
  31. {
  32. IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
  33. if (TryGetOffset(context.ResourceManager, operation, StorageKind.Input, out int inputOffset))
  34. {
  35. Operand primVertex = ioVariable == IoVariable.UserDefined
  36. ? operation.GetSource(2)
  37. : operation.GetSource(1);
  38. Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, inputOffset, primVertex);
  39. newNode = node.List.AddBefore(node, new Operation(
  40. Instruction.Load,
  41. StorageKind.StorageBuffer,
  42. operation.Dest,
  43. new[] { Const(context.ResourceManager.Reservations.VertexOutputStorageBufferBinding), Const(0), vertexElemOffset }));
  44. }
  45. else
  46. {
  47. switch (ioVariable)
  48. {
  49. case IoVariable.InvocationId:
  50. newNode = GenerateInvocationId(node, operation.Dest);
  51. break;
  52. case IoVariable.PrimitiveId:
  53. newNode = GeneratePrimitiveId(context.ResourceManager, node, operation.Dest);
  54. break;
  55. case IoVariable.GlobalId:
  56. case IoVariable.SubgroupEqMask:
  57. case IoVariable.SubgroupGeMask:
  58. case IoVariable.SubgroupGtMask:
  59. case IoVariable.SubgroupLaneId:
  60. case IoVariable.SubgroupLeMask:
  61. case IoVariable.SubgroupLtMask:
  62. // Those are valid or expected for geometry shaders.
  63. break;
  64. default:
  65. context.GpuAccessor.Log($"Invalid input \"{ioVariable}\".");
  66. break;
  67. }
  68. }
  69. }
  70. else if (operation.StorageKind == StorageKind.Output)
  71. {
  72. if (TryGetOffset(context.ResourceManager, operation, StorageKind.Output, out int outputOffset))
  73. {
  74. newNode = node.List.AddBefore(node, new Operation(
  75. Instruction.Load,
  76. StorageKind.LocalMemory,
  77. operation.Dest,
  78. new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset) }));
  79. }
  80. else
  81. {
  82. context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
  83. }
  84. }
  85. break;
  86. case Instruction.Store:
  87. if (operation.StorageKind == StorageKind.Output)
  88. {
  89. if (TryGetOffset(context.ResourceManager, operation, StorageKind.Output, out int outputOffset))
  90. {
  91. Operand value = operation.GetSource(operation.SourcesCount - 1);
  92. newNode = node.List.AddBefore(node, new Operation(
  93. Instruction.Store,
  94. StorageKind.LocalMemory,
  95. (Operand)null,
  96. new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset), value }));
  97. }
  98. else
  99. {
  100. context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
  101. }
  102. }
  103. break;
  104. }
  105. if (newNode != node)
  106. {
  107. Utils.DeleteNode(node, operation);
  108. }
  109. return newNode;
  110. }
  111. private static LinkedListNode<INode> GenerateEmitVertex(ShaderDefinitions definitions, ResourceManager resourceManager, LinkedListNode<INode> node)
  112. {
  113. int vbOutputBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;
  114. int ibOutputBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding;
  115. int stride = resourceManager.Reservations.OutputSizePerInvocation;
  116. Operand outputPrimVertex = IncrementLocalMemory(node, resourceManager.LocalGeometryOutputVertexCountMemoryId);
  117. Operand baseVertexOffset = GenerateBaseOffset(
  118. resourceManager,
  119. node,
  120. definitions.MaxOutputVertices * definitions.ThreadsPerInputPrimitive,
  121. definitions.ThreadsPerInputPrimitive);
  122. Operand outputBaseVertex = Local();
  123. node.List.AddBefore(node, new Operation(Instruction.Add, outputBaseVertex, new[] { baseVertexOffset, outputPrimVertex }));
  124. Operand outputPrimIndex = IncrementLocalMemory(node, resourceManager.LocalGeometryOutputIndexCountMemoryId);
  125. Operand baseIndexOffset = GenerateBaseOffset(
  126. resourceManager,
  127. node,
  128. definitions.GetGeometryOutputIndexBufferStride(),
  129. definitions.ThreadsPerInputPrimitive);
  130. Operand outputBaseIndex = Local();
  131. node.List.AddBefore(node, new Operation(Instruction.Add, outputBaseIndex, new[] { baseIndexOffset, outputPrimIndex }));
  132. node.List.AddBefore(node, new Operation(
  133. Instruction.Store,
  134. StorageKind.StorageBuffer,
  135. null,
  136. new[] { Const(ibOutputBinding), Const(0), outputBaseIndex, outputBaseVertex }));
  137. Operand baseOffset = Local();
  138. node.List.AddBefore(node, new Operation(Instruction.Multiply, baseOffset, new[] { outputBaseVertex, Const(stride) }));
  139. LinkedListNode<INode> newNode = node;
  140. for (int offset = 0; offset < stride; offset++)
  141. {
  142. Operand vertexOffset;
  143. if (offset > 0)
  144. {
  145. vertexOffset = Local();
  146. node.List.AddBefore(node, new Operation(Instruction.Add, vertexOffset, new[] { baseOffset, Const(offset) }));
  147. }
  148. else
  149. {
  150. vertexOffset = baseOffset;
  151. }
  152. Operand value = Local();
  153. node.List.AddBefore(node, new Operation(
  154. Instruction.Load,
  155. StorageKind.LocalMemory,
  156. value,
  157. new[] { Const(resourceManager.LocalVertexDataMemoryId), Const(offset) }));
  158. newNode = node.List.AddBefore(node, new Operation(
  159. Instruction.Store,
  160. StorageKind.StorageBuffer,
  161. null,
  162. new[] { Const(vbOutputBinding), Const(0), vertexOffset, value }));
  163. }
  164. return newNode;
  165. }
  166. private static LinkedListNode<INode> GenerateEndPrimitive(ShaderDefinitions definitions, ResourceManager resourceManager, LinkedListNode<INode> node)
  167. {
  168. int ibOutputBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding;
  169. Operand outputPrimIndex = IncrementLocalMemory(node, resourceManager.LocalGeometryOutputIndexCountMemoryId);
  170. Operand baseIndexOffset = GenerateBaseOffset(
  171. resourceManager,
  172. node,
  173. definitions.GetGeometryOutputIndexBufferStride(),
  174. definitions.ThreadsPerInputPrimitive);
  175. Operand outputBaseIndex = Local();
  176. node.List.AddBefore(node, new Operation(Instruction.Add, outputBaseIndex, new[] { baseIndexOffset, outputPrimIndex }));
  177. return node.List.AddBefore(node, new Operation(
  178. Instruction.Store,
  179. StorageKind.StorageBuffer,
  180. null,
  181. new[] { Const(ibOutputBinding), Const(0), outputBaseIndex, Const(-1) }));
  182. }
  183. private static Operand GenerateBaseOffset(ResourceManager resourceManager, LinkedListNode<INode> node, int stride, int threadsPerInputPrimitive)
  184. {
  185. Operand primitiveId = Local();
  186. GeneratePrimitiveId(resourceManager, node, primitiveId);
  187. Operand baseOffset = Local();
  188. node.List.AddBefore(node, new Operation(Instruction.Multiply, baseOffset, new[] { primitiveId, Const(stride) }));
  189. Operand invocationId = Local();
  190. GenerateInvocationId(node, invocationId);
  191. Operand invocationOffset = Local();
  192. node.List.AddBefore(node, new Operation(Instruction.Multiply, invocationOffset, new[] { invocationId, Const(stride / threadsPerInputPrimitive) }));
  193. Operand combinedOffset = Local();
  194. node.List.AddBefore(node, new Operation(Instruction.Add, combinedOffset, new[] { baseOffset, invocationOffset }));
  195. return combinedOffset;
  196. }
  197. private static Operand IncrementLocalMemory(LinkedListNode<INode> node, int memoryId)
  198. {
  199. Operand oldValue = Local();
  200. node.List.AddBefore(node, new Operation(
  201. Instruction.Load,
  202. StorageKind.LocalMemory,
  203. oldValue,
  204. new[] { Const(memoryId) }));
  205. Operand newValue = Local();
  206. node.List.AddBefore(node, new Operation(Instruction.Add, newValue, new[] { oldValue, Const(1) }));
  207. node.List.AddBefore(node, new Operation(Instruction.Store, StorageKind.LocalMemory, null, new[] { Const(memoryId), newValue }));
  208. return oldValue;
  209. }
  210. private static Operand GenerateVertexOffset(
  211. ResourceManager resourceManager,
  212. LinkedListNode<INode> node,
  213. int elementOffset,
  214. Operand primVertex)
  215. {
  216. int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
  217. Operand vertexCount = Local();
  218. node.List.AddBefore(node, new Operation(
  219. Instruction.Load,
  220. StorageKind.ConstantBuffer,
  221. vertexCount,
  222. new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(0) }));
  223. Operand primInputVertex = Local();
  224. node.List.AddBefore(node, new Operation(
  225. Instruction.Load,
  226. StorageKind.LocalMemory,
  227. primInputVertex,
  228. new[] { Const(resourceManager.LocalTopologyRemapMemoryId), primVertex }));
  229. Operand instanceIndex = Local();
  230. node.List.AddBefore(node, new Operation(
  231. Instruction.Load,
  232. StorageKind.Input,
  233. instanceIndex,
  234. new[] { Const((int)IoVariable.GlobalId), Const(1) }));
  235. Operand baseVertex = Local();
  236. node.List.AddBefore(node, new Operation(Instruction.Multiply, baseVertex, new[] { instanceIndex, vertexCount }));
  237. Operand vertexIndex = Local();
  238. node.List.AddBefore(node, new Operation(Instruction.Add, vertexIndex, new[] { baseVertex, primInputVertex }));
  239. Operand vertexBaseOffset = Local();
  240. node.List.AddBefore(node, new Operation(
  241. Instruction.Multiply,
  242. vertexBaseOffset,
  243. new[] { vertexIndex, Const(resourceManager.Reservations.InputSizePerInvocation) }));
  244. Operand vertexElemOffset;
  245. if (elementOffset != 0)
  246. {
  247. vertexElemOffset = Local();
  248. node.List.AddBefore(node, new Operation(Instruction.Add, vertexElemOffset, new[] { vertexBaseOffset, Const(elementOffset) }));
  249. }
  250. else
  251. {
  252. vertexElemOffset = vertexBaseOffset;
  253. }
  254. return vertexElemOffset;
  255. }
  256. private static LinkedListNode<INode> GeneratePrimitiveId(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
  257. {
  258. int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
  259. Operand vertexCount = Local();
  260. node.List.AddBefore(node, new Operation(
  261. Instruction.Load,
  262. StorageKind.ConstantBuffer,
  263. vertexCount,
  264. new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(0) }));
  265. Operand vertexIndex = Local();
  266. node.List.AddBefore(node, new Operation(
  267. Instruction.Load,
  268. StorageKind.Input,
  269. vertexIndex,
  270. new[] { Const((int)IoVariable.GlobalId), Const(0) }));
  271. Operand instanceIndex = Local();
  272. node.List.AddBefore(node, new Operation(
  273. Instruction.Load,
  274. StorageKind.Input,
  275. instanceIndex,
  276. new[] { Const((int)IoVariable.GlobalId), Const(1) }));
  277. Operand baseVertex = Local();
  278. node.List.AddBefore(node, new Operation(Instruction.Multiply, baseVertex, new[] { instanceIndex, vertexCount }));
  279. return node.List.AddBefore(node, new Operation(Instruction.Add, dest, new[] { baseVertex, vertexIndex }));
  280. }
  281. private static LinkedListNode<INode> GenerateInvocationId(LinkedListNode<INode> node, Operand dest)
  282. {
  283. return node.List.AddBefore(node, new Operation(
  284. Instruction.Load,
  285. StorageKind.Input,
  286. dest,
  287. new[] { Const((int)IoVariable.GlobalId), Const(2) }));
  288. }
  289. private static bool TryGetOffset(ResourceManager resourceManager, Operation operation, StorageKind storageKind, out int outputOffset)
  290. {
  291. bool isStore = operation.Inst == Instruction.Store;
  292. IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
  293. bool isValidOutput;
  294. if (ioVariable == IoVariable.UserDefined)
  295. {
  296. int lastIndex = operation.SourcesCount - (isStore ? 2 : 1);
  297. int location = operation.GetSource(1).Value;
  298. int component = operation.GetSource(lastIndex).Value;
  299. isValidOutput = resourceManager.Reservations.TryGetOffset(storageKind, location, component, out outputOffset);
  300. }
  301. else
  302. {
  303. if (ResourceReservations.IsVectorOrArrayVariable(ioVariable))
  304. {
  305. int component = operation.GetSource(operation.SourcesCount - (isStore ? 2 : 1)).Value;
  306. isValidOutput = resourceManager.Reservations.TryGetOffset(storageKind, ioVariable, component, out outputOffset);
  307. }
  308. else
  309. {
  310. isValidOutput = resourceManager.Reservations.TryGetOffset(storageKind, ioVariable, out outputOffset);
  311. }
  312. }
  313. return isValidOutput;
  314. }
  315. }
  316. }