AttributeMap.cs
  1  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2  using Ryujinx.Graphics.Shader.Translation;
  3  using System.Collections.Generic;
  4  using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  5  
  6  namespace Ryujinx.Graphics.Shader.Instructions
  7  {
  8      static class AttributeMap
  9      {
 10          private enum StagesMask : byte
 11          {
 12              None = 0,
 13              Compute = 1 << (int)ShaderStage.Compute,
 14              Vertex = 1 << (int)ShaderStage.Vertex,
 15              TessellationControl = 1 << (int)ShaderStage.TessellationControl,
 16              TessellationEvaluation = 1 << (int)ShaderStage.TessellationEvaluation,
 17              Geometry = 1 << (int)ShaderStage.Geometry,
 18              Fragment = 1 << (int)ShaderStage.Fragment,
 19  
 20              Tessellation = TessellationControl | TessellationEvaluation,
 21              VertexTessellationGeometry = Vertex | Tessellation | Geometry,
 22              TessellationGeometryFragment = Tessellation | Geometry | Fragment,
 23              AllGraphics = Vertex | Tessellation | Geometry | Fragment,
 24          }
 25  
 26          private readonly struct AttributeEntry
 27          {
 28              public int BaseOffset { get; }
 29              public AggregateType Type { get; }
 30              public IoVariable IoVariable { get; }
 31              public StagesMask InputMask { get; }
 32              public StagesMask OutputMask { get; }
 33  
 34              public AttributeEntry(
 35                  int baseOffset,
 36                  AggregateType type,
 37                  IoVariable ioVariable,
 38                  StagesMask inputMask,
 39                  StagesMask outputMask)
 40              {
 41                  BaseOffset = baseOffset;
 42                  Type = type;
 43                  IoVariable = ioVariable;
 44                  InputMask = inputMask;
 45                  OutputMask = outputMask;
 46              }
 47          }
 48  
 49          private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributes;
 50          private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributesPerPatch;
 51  
 52          static AttributeMap()
 53          {
 54              _attributes = CreateMap();
 55              _attributesPerPatch = CreatePerPatchMap();
 56          }
 57  
 58          private static IReadOnlyDictionary<int, AttributeEntry> CreateMap()
 59          {
 60              var map = new Dictionary<int, AttributeEntry>();
 61  
 62              Add(map, 0x060, AggregateType.S32, IoVariable.PrimitiveId, StagesMask.TessellationGeometryFragment, StagesMask.Geometry);
 63              Add(map, 0x064, AggregateType.S32, IoVariable.Layer, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
 64              Add(map, 0x068, AggregateType.S32, IoVariable.ViewportIndex, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
 65              Add(map, 0x06c, AggregateType.FP32, IoVariable.PointSize, StagesMask.None, StagesMask.VertexTessellationGeometry);
 66              Add(map, 0x070, AggregateType.Vector4 | AggregateType.FP32, IoVariable.Position, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 67              Add(map, 0x080, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.AllGraphics, StagesMask.VertexTessellationGeometry, 32);
 68              Add(map, 0x280, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 69              Add(map, 0x290, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 70              Add(map, 0x2a0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 71              Add(map, 0x2b0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 72              Add(map, 0x2c0, AggregateType.Array | AggregateType.FP32, IoVariable.ClipDistance, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry, 8);
 73              Add(map, 0x2e0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.PointCoord, StagesMask.Fragment, StagesMask.None);
 74              Add(map, 0x2e8, AggregateType.FP32, IoVariable.FogCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 75              Add(map, 0x2f0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationCoord, StagesMask.TessellationEvaluation, StagesMask.None);
 76              Add(map, 0x2f8, AggregateType.S32, IoVariable.InstanceId, StagesMask.Vertex, StagesMask.None);
 77              Add(map, 0x2fc, AggregateType.S32, IoVariable.VertexId, StagesMask.Vertex, StagesMask.None);
 78              Add(map, 0x300, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TextureCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
 79              Add(map, 0x3a0, AggregateType.Array | AggregateType.S32, IoVariable.ViewportMask, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
 80              Add(map, 0x3fc, AggregateType.Bool, IoVariable.FrontFacing, StagesMask.Fragment, StagesMask.None);
 81  
 82              return map;
 83          }
 84  
 85          private static IReadOnlyDictionary<int, AttributeEntry> CreatePerPatchMap()
 86          {
 87              var map = new Dictionary<int, AttributeEntry>();
 88  
 89              Add(map, 0x000, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TessellationLevelOuter, StagesMask.TessellationEvaluation, StagesMask.TessellationControl);
 90              Add(map, 0x010, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationLevelInner, StagesMask.TessellationEvaluation, StagesMask.TessellationControl);
 91              Add(map, 0x018, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.TessellationEvaluation, StagesMask.TessellationControl, 31, 0x200);
 92  
 93              return map;
 94          }
 95  
 96          private static void Add(
 97              Dictionary<int, AttributeEntry> attributes,
 98              int offset,
 99              AggregateType type,
100              IoVariable ioVariable,
101              StagesMask inputMask,
102              StagesMask outputMask,
103              int count = 1,
104              int upperBound = 0x400)
105          {
106              int baseOffset = offset;
107  
108              int elementsCount = GetElementCount(type);
109  
110              for (int index = 0; index < count; index++)
111              {
112                  for (int elementIndex = 0; elementIndex < elementsCount; elementIndex++)
113                  {
114                      attributes.Add(offset, new AttributeEntry(baseOffset, type, ioVariable, inputMask, outputMask));
115  
116                      offset += 4;
117  
118                      if (offset >= upperBound)
119                      {
120                          return;
121                      }
122                  }
123              }
124          }
125  
126          public static Operand GenerateAttributeLoad(EmitterContext context, Operand primVertex, int offset, bool isOutput, bool isPerPatch)
127          {
128              if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry))
129              {
130                  context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid.");
131                  return Const(0);
132              }
133  
134              StagesMask validUseMask = isOutput ? entry.OutputMask : entry.InputMask;
135  
136              if (((StagesMask)(1 << (int)context.TranslatorContext.Definitions.Stage) & validUseMask) == StagesMask.None)
137              {
138                  context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.TranslatorContext.Definitions.Stage}.");
139                  return Const(0);
140              }
141  
142              if (!IsSupportedByHost(context.TranslatorContext.GpuAccessor, context.TranslatorContext.Definitions.Stage, entry.IoVariable))
143              {
144                  context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.TranslatorContext.Definitions.Stage}.");
145                  return Const(0);
146              }
147  
148              if (HasInvocationId(context.TranslatorContext.Definitions.Stage, isOutput) && !isPerPatch)
149              {
150                  primVertex = context.Load(StorageKind.Input, IoVariable.InvocationId);
151              }
152  
153              int innerOffset = offset - entry.BaseOffset;
154              int innerIndex = innerOffset / 4;
155  
156              StorageKind storageKind = isPerPatch
157                  ? (isOutput ? StorageKind.OutputPerPatch : StorageKind.InputPerPatch)
158                  : (isOutput ? StorageKind.Output : StorageKind.Input);
159              IoVariable ioVariable = GetIoVariable(context.TranslatorContext.Definitions.Stage, in entry);
160              AggregateType type = GetType(context.TranslatorContext.Definitions, isOutput, innerIndex, in entry);
161              int elementCount = GetElementCount(type);
162  
163              bool isArray = type.HasFlag(AggregateType.Array);
164              bool hasArrayIndex = isArray || context.TranslatorContext.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput);
165  
166              bool hasElementIndex = elementCount > 1;
167  
168              if (hasArrayIndex && hasElementIndex)
169              {
170                  int arrayIndex = innerIndex / elementCount;
171                  int elementIndex = innerIndex - (arrayIndex * elementCount);
172  
173                  return primVertex == null || isArray
174                      ? context.Load(storageKind, ioVariable, primVertex, Const(arrayIndex), Const(elementIndex))
175                      : context.Load(storageKind, ioVariable, Const(arrayIndex), primVertex, Const(elementIndex));
176              }
177              else if (hasArrayIndex || hasElementIndex)
178              {
179                  return primVertex == null || isArray || !hasArrayIndex
180                      ? context.Load(storageKind, ioVariable, primVertex, Const(innerIndex))
181                      : context.Load(storageKind, ioVariable, Const(innerIndex), primVertex);
182              }
183              else
184              {
185                  return context.Load(storageKind, ioVariable, primVertex);
186              }
187          }
188  
189          public static void GenerateAttributeStore(EmitterContext context, int offset, bool isPerPatch, Operand value)
190          {
191              if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry))
192              {
193                  context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid.");
194                  return;
195              }
196  
197              if (((StagesMask)(1 << (int)context.TranslatorContext.Definitions.Stage) & entry.OutputMask) == StagesMask.None)
198              {
199                  context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.TranslatorContext.Definitions.Stage}.");
200                  return;
201              }
202  
203              if (!IsSupportedByHost(context.TranslatorContext.GpuAccessor, context.TranslatorContext.Definitions.Stage, entry.IoVariable))
204              {
205                  context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.TranslatorContext.Definitions.Stage}.");
206                  return;
207              }
208  
209              Operand invocationId = null;
210  
211              if (HasInvocationId(context.TranslatorContext.Definitions.Stage, isOutput: true) && !isPerPatch)
212              {
213                  invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
214              }
215  
216              int innerOffset = offset - entry.BaseOffset;
217              int innerIndex = innerOffset / 4;
218  
219              StorageKind storageKind = isPerPatch ? StorageKind.OutputPerPatch : StorageKind.Output;
220              IoVariable ioVariable = GetIoVariable(context.TranslatorContext.Definitions.Stage, in entry);
221              AggregateType type = GetType(context.TranslatorContext.Definitions, isOutput: true, innerIndex, in entry);
222              int elementCount = GetElementCount(type);
223  
224              bool isArray = type.HasFlag(AggregateType.Array);
225              bool hasArrayIndex = isArray || context.TranslatorContext.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput: true);
226  
227              bool hasElementIndex = elementCount > 1;
228  
229              if (hasArrayIndex && hasElementIndex)
230              {
231                  int arrayIndex = innerIndex / elementCount;
232                  int elementIndex = innerIndex - (arrayIndex * elementCount);
233  
234                  if (invocationId == null || isArray)
235                  {
236                      context.Store(storageKind, ioVariable, invocationId, Const(arrayIndex), Const(elementIndex), value);
237                  }
238                  else
239                  {
240                      context.Store(storageKind, ioVariable, Const(arrayIndex), invocationId, Const(elementIndex), value);
241                  }
242              }
243              else if (hasArrayIndex || hasElementIndex)
244              {
245                  if (invocationId == null || isArray || !hasArrayIndex)
246                  {
247                      context.Store(storageKind, ioVariable, invocationId, Const(innerIndex), value);
248                  }
249                  else
250                  {
251                      context.Store(storageKind, ioVariable, Const(innerIndex), invocationId, value);
252                  }
253              }
254              else
255              {
256                  context.Store(storageKind, ioVariable, invocationId, value);
257              }
258          }
259  
260          private static bool IsSupportedByHost(IGpuAccessor gpuAccessor, ShaderStage stage, IoVariable ioVariable)
261          {
262              if (ioVariable == IoVariable.ViewportIndex && stage != ShaderStage.Geometry && stage != ShaderStage.Fragment)
263              {
264                  return gpuAccessor.QueryHostSupportsViewportIndexVertexTessellation();
265              }
266              else if (ioVariable == IoVariable.ViewportMask)
267              {
268                  return gpuAccessor.QueryHostSupportsViewportMask();
269              }
270  
271              return true;
272          }
273  
274          public static IoVariable GetIoVariable(ShaderDefinitions definitions, int offset, out int location)
275          {
276              location = 0;
277  
278              if (!_attributes.TryGetValue(offset, out AttributeEntry entry))
279              {
280                  return IoVariable.Invalid;
281              }
282  
283              if (((StagesMask)(1 << (int)definitions.Stage) & entry.OutputMask) == StagesMask.None)
284              {
285                  return IoVariable.Invalid;
286              }
287  
288              if (definitions.HasPerLocationInputOrOutput(entry.IoVariable, isOutput: true))
289              {
290                  location = (offset - entry.BaseOffset) / 16;
291              }
292  
293              return GetIoVariable(definitions.Stage, in entry);
294          }
295  
296          private static IoVariable GetIoVariable(ShaderStage stage, in AttributeEntry entry)
297          {
298              if (entry.IoVariable == IoVariable.Position && stage == ShaderStage.Fragment)
299              {
300                  return IoVariable.FragmentCoord;
301              }
302  
303              return entry.IoVariable;
304          }
305  
306          private static AggregateType GetType(ShaderDefinitions definitions, bool isOutput, int innerIndex, in AttributeEntry entry)
307          {
308              AggregateType type = entry.Type;
309  
310              if (entry.IoVariable == IoVariable.UserDefined)
311              {
312                  type = definitions.GetUserDefinedType(innerIndex / 4, isOutput);
313              }
314              else if (entry.IoVariable == IoVariable.FragmentOutputColor)
315              {
316                  type = definitions.GetFragmentOutputColorType(innerIndex / 4);
317              }
318  
319              return type;
320          }
321  
322          public static bool HasPrimitiveVertex(ShaderStage stage, bool isOutput)
323          {
324              if (isOutput)
325              {
326                  return false;
327              }
328  
329              return stage == ShaderStage.TessellationControl ||
330                     stage == ShaderStage.TessellationEvaluation ||
331                     stage == ShaderStage.Geometry;
332          }
333  
334          public static bool HasInvocationId(ShaderStage stage, bool isOutput)
335          {
336              return isOutput && stage == ShaderStage.TessellationControl;
337          }
338  
339          private static int GetElementCount(AggregateType type)
340          {
341              return (type & AggregateType.ElementCountMask) switch
342              {
343                  AggregateType.Vector2 => 2,
344                  AggregateType.Vector3 => 3,
345                  AggregateType.Vector4 => 4,
346                  _ => 1,
347              };
348          }
349      }
350  }