/ src / Ryujinx.Graphics.Shader / Translation / ShaderDefinitions.cs
ShaderDefinitions.cs
  1  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2  using System;
  3  using System.Collections.Generic;
  4  using System.Numerics;
  5  
  6  namespace Ryujinx.Graphics.Shader.Translation
  7  {
  8      class ShaderDefinitions
  9      {
 10          private readonly GpuGraphicsState _graphicsState;
 11  
 12          public ShaderStage Stage { get; }
 13  
 14          public int ComputeLocalSizeX { get; }
 15          public int ComputeLocalSizeY { get; }
 16          public int ComputeLocalSizeZ { get; }
 17  
 18          public bool TessCw => _graphicsState.TessCw;
 19          public TessPatchType TessPatchType => _graphicsState.TessPatchType;
 20          public TessSpacing TessSpacing => _graphicsState.TessSpacing;
 21  
 22          public bool AlphaToCoverageDitherEnable => _graphicsState.AlphaToCoverageEnable && _graphicsState.AlphaToCoverageDitherEnable;
 23          public bool ViewportTransformDisable => _graphicsState.ViewportTransformDisable;
 24  
 25          public bool DepthMode => _graphicsState.DepthMode;
 26  
 27          public float PointSize => _graphicsState.PointSize;
 28  
 29          public AlphaTestOp AlphaTestCompare => _graphicsState.AlphaTestCompare;
 30          public float AlphaTestReference => _graphicsState.AlphaTestReference;
 31  
 32          public bool GpPassthrough { get; }
 33          public bool LastInVertexPipeline { get; set; }
 34  
 35          public int ThreadsPerInputPrimitive { get; private set; }
 36  
 37          public InputTopology InputTopology => _graphicsState.Topology;
 38          public OutputTopology OutputTopology { get; }
 39  
 40          public int MaxOutputVertices { get; }
 41  
 42          public bool DualSourceBlend => _graphicsState.DualSourceBlendEnable;
 43          public bool EarlyZForce => _graphicsState.EarlyZForce;
 44  
 45          public bool YNegateEnabled => _graphicsState.YNegateEnabled;
 46          public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;
 47  
 48          public bool HalvePrimitiveId => _graphicsState.HalvePrimitiveId;
 49  
 50          public ImapPixelType[] ImapTypes { get; }
 51          public bool IaIndexing { get; private set; }
 52          public bool OaIndexing { get; private set; }
 53  
 54          public int OmapTargets { get; }
 55          public bool OmapSampleMask { get; }
 56          public bool OmapDepth { get; }
 57  
 58          public bool SupportsScaledVertexFormats { get; }
 59  
 60          public bool TransformFeedbackEnabled { get; }
 61  
 62          private readonly TransformFeedbackOutput[] _transformFeedbackOutputs;
 63  
 64          readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable>
 65          {
 66              public IoVariable IoVariable { get; }
 67              public int Location { get; }
 68              public int Component { get; }
 69  
 70              public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0)
 71              {
 72                  IoVariable = ioVariable;
 73                  Location = location;
 74                  Component = component;
 75              }
 76  
 77              public override bool Equals(object other)
 78              {
 79                  return other is TransformFeedbackVariable tfbVar && Equals(tfbVar);
 80              }
 81  
 82              public bool Equals(TransformFeedbackVariable other)
 83              {
 84                  return IoVariable == other.IoVariable &&
 85                      Location == other.Location &&
 86                      Component == other.Component;
 87              }
 88  
 89              public override int GetHashCode()
 90              {
 91                  return (int)IoVariable | (Location << 8) | (Component << 16);
 92              }
 93  
 94              public override string ToString()
 95              {
 96                  return $"{IoVariable}.{Location}.{Component}";
 97              }
 98          }
 99  
100          private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions;
101  
102          public ShaderDefinitions(ShaderStage stage, ulong transformFeedbackVecMap, TransformFeedbackOutput[] transformFeedbackOutputs)
103          {
104              Stage = stage;
105              TransformFeedbackEnabled = transformFeedbackOutputs != null;
106              _transformFeedbackOutputs = transformFeedbackOutputs;
107              _transformFeedbackDefinitions = new();
108  
109              PopulateTransformFeedbackDefinitions(transformFeedbackVecMap, transformFeedbackOutputs);
110          }
111  
112          public ShaderDefinitions(
113              ShaderStage stage,
114              int computeLocalSizeX,
115              int computeLocalSizeY,
116              int computeLocalSizeZ)
117          {
118              Stage = stage;
119              ComputeLocalSizeX = computeLocalSizeX;
120              ComputeLocalSizeY = computeLocalSizeY;
121              ComputeLocalSizeZ = computeLocalSizeZ;
122          }
123  
124          public ShaderDefinitions(
125              ShaderStage stage,
126              GpuGraphicsState graphicsState,
127              bool gpPassthrough,
128              int threadsPerInputPrimitive,
129              OutputTopology outputTopology,
130              int maxOutputVertices)
131          {
132              Stage = stage;
133              _graphicsState = graphicsState;
134              GpPassthrough = gpPassthrough;
135              ThreadsPerInputPrimitive = threadsPerInputPrimitive;
136              OutputTopology = outputTopology;
137              MaxOutputVertices = maxOutputVertices;
138          }
139  
140          public ShaderDefinitions(
141              ShaderStage stage,
142              GpuGraphicsState graphicsState,
143              bool gpPassthrough,
144              int threadsPerInputPrimitive,
145              OutputTopology outputTopology,
146              int maxOutputVertices,
147              ImapPixelType[] imapTypes,
148              int omapTargets,
149              bool omapSampleMask,
150              bool omapDepth,
151              bool supportsScaledVertexFormats,
152              ulong transformFeedbackVecMap,
153              TransformFeedbackOutput[] transformFeedbackOutputs)
154          {
155              Stage = stage;
156              _graphicsState = graphicsState;
157              GpPassthrough = gpPassthrough;
158              ThreadsPerInputPrimitive = threadsPerInputPrimitive;
159              OutputTopology = outputTopology;
160              MaxOutputVertices = gpPassthrough ? graphicsState.Topology.ToInputVerticesNoAdjacency() : maxOutputVertices;
161              ImapTypes = imapTypes;
162              OmapTargets = omapTargets;
163              OmapSampleMask = omapSampleMask;
164              OmapDepth = omapDepth;
165              LastInVertexPipeline = stage < ShaderStage.Fragment;
166              SupportsScaledVertexFormats = supportsScaledVertexFormats;
167              TransformFeedbackEnabled = transformFeedbackOutputs != null;
168              _transformFeedbackOutputs = transformFeedbackOutputs;
169              _transformFeedbackDefinitions = new();
170  
171              PopulateTransformFeedbackDefinitions(transformFeedbackVecMap, transformFeedbackOutputs);
172          }
173  
174          private void PopulateTransformFeedbackDefinitions(ulong transformFeedbackVecMap, TransformFeedbackOutput[] transformFeedbackOutputs)
175          {
176              while (transformFeedbackVecMap != 0)
177              {
178                  int vecIndex = BitOperations.TrailingZeroCount(transformFeedbackVecMap);
179  
180                  for (int subIndex = 0; subIndex < 4; subIndex++)
181                  {
182                      int wordOffset = vecIndex * 4 + subIndex;
183                      int byteOffset = wordOffset * 4;
184  
185                      if (transformFeedbackOutputs[wordOffset].Valid)
186                      {
187                          IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location);
188                          int component = 0;
189  
190                          if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true))
191                          {
192                              component = subIndex;
193                          }
194  
195                          var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
196                          _transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]);
197                      }
198                  }
199  
200                  transformFeedbackVecMap &= ~(1UL << vecIndex);
201              }
202          }
203  
204          public void EnableInputIndexing()
205          {
206              IaIndexing = true;
207          }
208  
209          public void EnableOutputIndexing()
210          {
211              OaIndexing = true;
212          }
213  
214          public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput)
215          {
216              if (!HasTransformFeedbackOutputs())
217              {
218                  transformFeedbackOutput = default;
219                  return false;
220              }
221  
222              var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
223              return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput);
224          }
225  
226          private bool HasTransformFeedbackOutputs()
227          {
228              return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment);
229          }
230  
231          public bool HasTransformFeedbackOutputs(bool isOutput)
232          {
233              return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment));
234          }
235  
236          public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput)
237          {
238              if (ioVariable == IoVariable.UserDefined)
239              {
240                  return (!isOutput && !IaIndexing) || (isOutput && !OaIndexing);
241              }
242  
243              return ioVariable == IoVariable.FragmentOutputColor;
244          }
245  
246          public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput)
247          {
248              if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput))
249              {
250                  return false;
251              }
252  
253              return GetTransformFeedbackOutputComponents(location, component) == 1;
254          }
255  
256          public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset)
257          {
258              return _transformFeedbackOutputs[wordOffset];
259          }
260  
261          public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
262          {
263              return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component);
264          }
265  
266          public int GetTransformFeedbackOutputComponents(int location, int component)
267          {
268              int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4;
269              int index = baseIndex + component;
270              int count = 1;
271  
272              for (; count < 4; count++)
273              {
274                  ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1];
275                  ref var curr = ref _transformFeedbackOutputs[baseIndex + count];
276  
277                  int prevOffset = prev.Offset;
278                  int currOffset = curr.Offset;
279  
280                  if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
281                  {
282                      break;
283                  }
284              }
285  
286              if (baseIndex + count <= index)
287              {
288                  return 1;
289              }
290  
291              return count;
292          }
293  
294          public AggregateType GetFragmentOutputColorType(int location)
295          {
296              return AggregateType.Vector4 | _graphicsState.FragmentOutputTypes[location].ToAggregateType();
297          }
298  
299          public AggregateType GetUserDefinedType(int location, bool isOutput)
300          {
301              if ((!isOutput && IaIndexing) || (isOutput && OaIndexing))
302              {
303                  return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32;
304              }
305  
306              AggregateType type = AggregateType.Vector4;
307  
308              if (Stage == ShaderStage.Vertex && !isOutput)
309              {
310                  type |= _graphicsState.AttributeTypes[location].ToAggregateType(SupportsScaledVertexFormats);
311              }
312              else
313              {
314                  type |= AggregateType.FP32;
315              }
316  
317              return type;
318          }
319  
320          public AttributeType GetAttributeType(int location)
321          {
322              return _graphicsState.AttributeTypes[location];
323          }
324  
325          public bool IsAttributeSint(int location)
326          {
327              return (_graphicsState.AttributeTypes[location] & ~AttributeType.AnyPacked) == AttributeType.Sint;
328          }
329  
330          public bool IsAttributePacked(int location)
331          {
332              return _graphicsState.AttributeTypes[location].HasFlag(AttributeType.Packed);
333          }
334  
335          public bool IsAttributePackedRgb10A2Signed(int location)
336          {
337              return _graphicsState.AttributeTypes[location].HasFlag(AttributeType.PackedRgb10A2Signed);
338          }
339  
340          public int GetGeometryOutputIndexBufferStridePerInstance()
341          {
342              return MaxOutputVertices + OutputTopology switch
343              {
344                  OutputTopology.LineStrip => MaxOutputVertices / 2,
345                  OutputTopology.TriangleStrip => MaxOutputVertices / 3,
346                  _ => MaxOutputVertices,
347              };
348          }
349  
350          public int GetGeometryOutputIndexBufferStride()
351          {
352              return GetGeometryOutputIndexBufferStridePerInstance() * ThreadsPerInputPrimitive;
353          }
354      }
355  }