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 }