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 }