AttributeMap.cs 16 KB

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