VertexToCompute.cs
  1  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2  using Ryujinx.Graphics.Shader.Translation.Optimizations;
  3  using System.Collections.Generic;
  4  
  5  using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  6  
  7  namespace Ryujinx.Graphics.Shader.Translation.Transforms
  8  {
  9      class VertexToCompute : ITransformPass
 10      {
 11          public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures)
 12          {
 13              return usedFeatures.HasFlag(FeatureFlags.VtgAsCompute);
 14          }
 15  
 16          public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node)
 17          {
 18              if (context.Definitions.Stage != ShaderStage.Vertex)
 19              {
 20                  return node;
 21              }
 22  
 23              Operation operation = (Operation)node.Value;
 24  
 25              LinkedListNode<INode> newNode = node;
 26  
 27              if (operation.Inst == Instruction.Load && operation.StorageKind == StorageKind.Input)
 28              {
 29                  Operand dest = operation.Dest;
 30  
 31                  switch ((IoVariable)operation.GetSource(0).Value)
 32                  {
 33                      case IoVariable.BaseInstance:
 34                          newNode = GenerateBaseInstanceLoad(context.ResourceManager, node, dest);
 35                          break;
 36                      case IoVariable.BaseVertex:
 37                          newNode = GenerateBaseVertexLoad(context.ResourceManager, node, dest);
 38                          break;
 39                      case IoVariable.InstanceId:
 40                          newNode = GenerateInstanceIdLoad(node, dest);
 41                          break;
 42                      case IoVariable.InstanceIndex:
 43                          newNode = GenerateInstanceIndexLoad(context.ResourceManager, node, dest);
 44                          break;
 45                      case IoVariable.VertexId:
 46                      case IoVariable.VertexIndex:
 47                          newNode = GenerateVertexIndexLoad(context.ResourceManager, node, dest);
 48                          break;
 49                      case IoVariable.UserDefined:
 50                          int location = operation.GetSource(1).Value;
 51                          int component = operation.GetSource(2).Value;
 52  
 53                          if (context.Definitions.IsAttributePacked(location))
 54                          {
 55                              bool needsSextNorm = context.Definitions.IsAttributePackedRgb10A2Signed(location);
 56  
 57                              SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
 58                              Operand temp = needsSextNorm ? Local() : dest;
 59                              Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, 0);
 60  
 61                              newNode = node.List.AddBefore(node, new TextureOperation(
 62                                  Instruction.TextureSample,
 63                                  SamplerType.TextureBuffer,
 64                                  TextureFormat.Unknown,
 65                                  TextureFlags.IntCoords,
 66                                  setAndBinding.SetIndex,
 67                                  setAndBinding.Binding,
 68                                  1 << component,
 69                                  new[] { temp },
 70                                  new[] { vertexElemOffset }));
 71  
 72                              if (needsSextNorm)
 73                              {
 74                                  bool sint = context.Definitions.IsAttributeSint(location);
 75                                  CopySignExtendedNormalized(node, component == 3 ? 2 : 10, !sint, dest, temp);
 76                              }
 77                          }
 78                          else
 79                          {
 80                              SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
 81                              Operand temp = component > 0 ? Local() : dest;
 82                              Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, component);
 83  
 84                              newNode = node.List.AddBefore(node, new TextureOperation(
 85                                  Instruction.TextureSample,
 86                                  SamplerType.TextureBuffer,
 87                                  TextureFormat.Unknown,
 88                                  TextureFlags.IntCoords,
 89                                  setAndBinding.SetIndex,
 90                                  setAndBinding.Binding,
 91                                  1,
 92                                  new[] { temp },
 93                                  new[] { vertexElemOffset }));
 94  
 95                              if (component > 0)
 96                              {
 97                                  newNode = CopyMasked(context.ResourceManager, newNode, location, component, dest, temp);
 98                              }
 99                          }
100                          break;
101                      case IoVariable.GlobalId:
102                      case IoVariable.SubgroupEqMask:
103                      case IoVariable.SubgroupGeMask:
104                      case IoVariable.SubgroupGtMask:
105                      case IoVariable.SubgroupLaneId:
106                      case IoVariable.SubgroupLeMask:
107                      case IoVariable.SubgroupLtMask:
108                          // Those are valid or expected for vertex shaders.
109                          break;
110                      default:
111                          context.GpuAccessor.Log($"Invalid input \"{(IoVariable)operation.GetSource(0).Value}\".");
112                          break;
113                  }
114              }
115              else if (operation.Inst == Instruction.Load && operation.StorageKind == StorageKind.Output)
116              {
117                  if (TryGetOutputOffset(context.ResourceManager, operation, out int outputOffset))
118                  {
119                      newNode = node.List.AddBefore(node, new Operation(
120                          Instruction.Load,
121                          StorageKind.LocalMemory,
122                          operation.Dest,
123                          new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset) }));
124                  }
125                  else
126                  {
127                      context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
128                  }
129              }
130              else if (operation.Inst == Instruction.Store && operation.StorageKind == StorageKind.Output)
131              {
132                  if (TryGetOutputOffset(context.ResourceManager, operation, out int outputOffset))
133                  {
134                      Operand value = operation.GetSource(operation.SourcesCount - 1);
135  
136                      newNode = node.List.AddBefore(node, new Operation(
137                          Instruction.Store,
138                          StorageKind.LocalMemory,
139                          (Operand)null,
140                          new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset), value }));
141                  }
142                  else
143                  {
144                      context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
145                  }
146              }
147  
148              if (newNode != node)
149              {
150                  Utils.DeleteNode(node, operation);
151              }
152  
153              return newNode;
154          }
155  
156          private static Operand GenerateVertexOffset(ResourceManager resourceManager, LinkedListNode<INode> node, int location, int component)
157          {
158              int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
159  
160              Operand vertexIdVr = Local();
161              GenerateVertexIdVertexRateLoad(resourceManager, node, vertexIdVr);
162  
163              Operand vertexIdIr = Local();
164              GenerateVertexIdInstanceRateLoad(resourceManager, node, vertexIdIr);
165  
166              Operand attributeOffset = Local();
167              node.List.AddBefore(node, new Operation(
168                  Instruction.Load,
169                  StorageKind.ConstantBuffer,
170                  attributeOffset,
171                  new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexOffsets), Const(location), Const(0) }));
172  
173              Operand isInstanceRate = Local();
174              node.List.AddBefore(node, new Operation(
175                  Instruction.Load,
176                  StorageKind.ConstantBuffer,
177                  isInstanceRate,
178                  new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexOffsets), Const(location), Const(1) }));
179  
180              Operand vertexId = Local();
181              node.List.AddBefore(node, new Operation(
182                  Instruction.ConditionalSelect,
183                  vertexId,
184                  new[] { isInstanceRate, vertexIdIr, vertexIdVr }));
185  
186              Operand vertexStride = Local();
187              node.List.AddBefore(node, new Operation(
188                  Instruction.Load,
189                  StorageKind.ConstantBuffer,
190                  vertexStride,
191                  new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexStrides), Const(location), Const(0) }));
192  
193              Operand vertexBaseOffset = Local();
194              node.List.AddBefore(node, new Operation(Instruction.Multiply, vertexBaseOffset, new[] { vertexId, vertexStride }));
195  
196              Operand vertexOffset = Local();
197              node.List.AddBefore(node, new Operation(Instruction.Add, vertexOffset, new[] { attributeOffset, vertexBaseOffset }));
198  
199              Operand vertexElemOffset;
200  
201              if (component != 0)
202              {
203                  vertexElemOffset = Local();
204  
205                  node.List.AddBefore(node, new Operation(Instruction.Add, vertexElemOffset, new[] { vertexOffset, Const(component) }));
206              }
207              else
208              {
209                  vertexElemOffset = vertexOffset;
210              }
211  
212              return vertexElemOffset;
213          }
214  
215          private static LinkedListNode<INode> CopySignExtendedNormalized(LinkedListNode<INode> node, int bits, bool normalize, Operand dest, Operand src)
216          {
217              Operand leftShifted = Local();
218              node = node.List.AddAfter(node, new Operation(
219                  Instruction.ShiftLeft,
220                  leftShifted,
221                  new[] { src, Const(32 - bits) }));
222  
223              Operand rightShifted = normalize ? Local() : dest;
224              node = node.List.AddAfter(node, new Operation(
225                  Instruction.ShiftRightS32,
226                  rightShifted,
227                  new[] { leftShifted, Const(32 - bits) }));
228  
229              if (normalize)
230              {
231                  Operand asFloat = Local();
232                  node = node.List.AddAfter(node, new Operation(Instruction.ConvertS32ToFP32, asFloat, new[] { rightShifted }));
233                  node = node.List.AddAfter(node, new Operation(
234                      Instruction.FP32 | Instruction.Multiply,
235                      dest,
236                      new[] { asFloat, ConstF(1f / (1 << (bits - 1))) }));
237              }
238  
239              return node;
240          }
241  
242          private static LinkedListNode<INode> CopyMasked(
243              ResourceManager resourceManager,
244              LinkedListNode<INode> node,
245              int location,
246              int component,
247              Operand dest,
248              Operand src)
249          {
250              Operand componentExists = Local();
251              int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
252              node = node.List.AddAfter(node, new Operation(
253                  Instruction.Load,
254                  StorageKind.ConstantBuffer,
255                  componentExists,
256                  new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexStrides), Const(location), Const(component) }));
257  
258              return node.List.AddAfter(node, new Operation(
259                  Instruction.ConditionalSelect,
260                  dest,
261                  new[] { componentExists, src, ConstF(component == 3 ? 1f : 0f) }));
262          }
263  
264          private static LinkedListNode<INode> GenerateBaseVertexLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
265          {
266              int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
267  
268              return node.List.AddBefore(node, new Operation(
269                  Instruction.Load,
270                  StorageKind.ConstantBuffer,
271                  dest,
272                  new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(2) }));
273          }
274  
275          private static LinkedListNode<INode> GenerateBaseInstanceLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
276          {
277              int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
278  
279              return node.List.AddBefore(node, new Operation(
280                  Instruction.Load,
281                  StorageKind.ConstantBuffer,
282                  dest,
283                  new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(3) }));
284          }
285  
286          private static LinkedListNode<INode> GenerateVertexIndexLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
287          {
288              Operand baseVertex = Local();
289              Operand vertexId = Local();
290  
291              GenerateBaseVertexLoad(resourceManager, node, baseVertex);
292              GenerateVertexIdVertexRateLoad(resourceManager, node, vertexId);
293  
294              return node.List.AddBefore(node, new Operation(Instruction.Add, dest, new[] { baseVertex, vertexId }));
295          }
296  
297          private static LinkedListNode<INode> GenerateInstanceIndexLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
298          {
299              Operand baseInstance = Local();
300              Operand instanceId = Local();
301  
302              GenerateBaseInstanceLoad(resourceManager, node, baseInstance);
303  
304              node.List.AddBefore(node, new Operation(
305                  Instruction.Load,
306                  StorageKind.Input,
307                  instanceId,
308                  new[] { Const((int)IoVariable.GlobalId), Const(1) }));
309  
310              return node.List.AddBefore(node, new Operation(Instruction.Add, dest, new[] { baseInstance, instanceId }));
311          }
312  
313          private static LinkedListNode<INode> GenerateVertexIdVertexRateLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
314          {
315              Operand[] sources = new Operand[] { Const(resourceManager.LocalVertexIndexVertexRateMemoryId) };
316  
317              return node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.LocalMemory, dest, sources));
318          }
319  
320          private static LinkedListNode<INode> GenerateVertexIdInstanceRateLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
321          {
322              Operand[] sources = new Operand[] { Const(resourceManager.LocalVertexIndexInstanceRateMemoryId) };
323  
324              return node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.LocalMemory, dest, sources));
325          }
326  
327          private static LinkedListNode<INode> GenerateInstanceIdLoad(LinkedListNode<INode> node, Operand dest)
328          {
329              Operand[] sources = new Operand[] { Const((int)IoVariable.GlobalId), Const(1) };
330  
331              return node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, dest, sources));
332          }
333  
334          private static bool TryGetOutputOffset(ResourceManager resourceManager, Operation operation, out int outputOffset)
335          {
336              bool isStore = operation.Inst == Instruction.Store;
337  
338              IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
339  
340              bool isValidOutput;
341  
342              if (ioVariable == IoVariable.UserDefined)
343              {
344                  int lastIndex = operation.SourcesCount - (isStore ? 2 : 1);
345  
346                  int location = operation.GetSource(1).Value;
347                  int component = operation.GetSource(lastIndex).Value;
348  
349                  isValidOutput = resourceManager.Reservations.TryGetOffset(StorageKind.Output, location, component, out outputOffset);
350              }
351              else
352              {
353                  if (ResourceReservations.IsVectorOrArrayVariable(ioVariable))
354                  {
355                      int component = operation.GetSource(operation.SourcesCount - (isStore ? 2 : 1)).Value;
356  
357                      isValidOutput = resourceManager.Reservations.TryGetOffset(StorageKind.Output, ioVariable, component, out outputOffset);
358                  }
359                  else
360                  {
361                      isValidOutput = resourceManager.Reservations.TryGetOffset(StorageKind.Output, ioVariable, out outputOffset);
362                  }
363              }
364  
365              return isValidOutput;
366          }
367      }
368  }