using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System.Collections.Generic; using System.Numerics; namespace Ryujinx.Graphics.Shader.Translation { public class ResourceReservations { public const int TfeBuffersCount = 4; public const int MaxVertexBufferTextures = 32; private const int TextureSetIndex = 2; // TODO: Get from GPU accessor. public int VertexInfoConstantBufferBinding { get; } public int VertexOutputStorageBufferBinding { get; } public int GeometryVertexOutputStorageBufferBinding { get; } public int GeometryIndexOutputStorageBufferBinding { get; } public int IndexBufferTextureBinding { get; } public int TopologyRemapBufferTextureBinding { get; } public int ReservedConstantBuffers { get; } public int ReservedStorageBuffers { get; } public int ReservedTextures { get; } public int ReservedImages { get; } public int InputSizePerInvocation { get; } public int OutputSizePerInvocation { get; } public int OutputSizeInBytesPerInvocation => OutputSizePerInvocation * sizeof(uint); private readonly int _tfeBufferSbBaseBinding; private readonly int _vertexBufferTextureBaseBinding; private readonly Dictionary _offsets; internal IReadOnlyDictionary Offsets => _offsets; internal ResourceReservations(bool isTransformFeedbackEmulated, bool vertexAsCompute) { // All stages reserves the first constant buffer binding for the support buffer. ReservedConstantBuffers = 1; ReservedStorageBuffers = 0; ReservedTextures = 0; ReservedImages = 0; if (isTransformFeedbackEmulated) { // Transform feedback emulation currently always uses 4 storage buffers. _tfeBufferSbBaseBinding = ReservedStorageBuffers; ReservedStorageBuffers = TfeBuffersCount; } if (vertexAsCompute) { // One constant buffer reserved for vertex related state. VertexInfoConstantBufferBinding = ReservedConstantBuffers++; // One storage buffer for the output vertex data. VertexOutputStorageBufferBinding = ReservedStorageBuffers++; // One storage buffer for the output geometry vertex data. GeometryVertexOutputStorageBufferBinding = ReservedStorageBuffers++; // One storage buffer for the output geometry index data. GeometryIndexOutputStorageBufferBinding = ReservedStorageBuffers++; // Enough textures reserved for all vertex attributes, plus the index buffer. IndexBufferTextureBinding = ReservedTextures; TopologyRemapBufferTextureBinding = ReservedTextures + 1; _vertexBufferTextureBaseBinding = ReservedTextures + 2; ReservedTextures += 2 + MaxVertexBufferTextures; } } internal ResourceReservations( IGpuAccessor gpuAccessor, bool isTransformFeedbackEmulated, bool vertexAsCompute, IoUsage? vacInput, IoUsage vacOutput) : this(isTransformFeedbackEmulated, vertexAsCompute) { if (vertexAsCompute) { _offsets = new(); if (vacInput.HasValue) { InputSizePerInvocation = FillIoOffsetMap(gpuAccessor, StorageKind.Input, vacInput.Value); } OutputSizePerInvocation = FillIoOffsetMap(gpuAccessor, StorageKind.Output, vacOutput); } } private int FillIoOffsetMap(IGpuAccessor gpuAccessor, StorageKind storageKind, IoUsage vacUsage) { int offset = 0; for (int c = 0; c < 4; c++) { _offsets.Add(new IoDefinition(storageKind, IoVariable.Position, 0, c), offset++); } _offsets.Add(new IoDefinition(storageKind, IoVariable.PointSize), offset++); int clipDistancesWrittenMap = vacUsage.ClipDistancesWritten; while (clipDistancesWrittenMap != 0) { int index = BitOperations.TrailingZeroCount(clipDistancesWrittenMap); _offsets.Add(new IoDefinition(storageKind, IoVariable.ClipDistance, 0, index), offset++); clipDistancesWrittenMap &= ~(1 << index); } if (vacUsage.UsesRtLayer) { _offsets.Add(new IoDefinition(storageKind, IoVariable.Layer), offset++); } if (vacUsage.UsesViewportIndex && gpuAccessor.QueryHostSupportsViewportIndexVertexTessellation()) { _offsets.Add(new IoDefinition(storageKind, IoVariable.VertexIndex), offset++); } if (vacUsage.UsesViewportMask && gpuAccessor.QueryHostSupportsViewportMask()) { _offsets.Add(new IoDefinition(storageKind, IoVariable.ViewportMask), offset++); } int usedDefinedMap = vacUsage.UserDefinedMap; while (usedDefinedMap != 0) { int location = BitOperations.TrailingZeroCount(usedDefinedMap); for (int c = 0; c < 4; c++) { _offsets.Add(new IoDefinition(storageKind, IoVariable.UserDefined, location, c), offset++); } usedDefinedMap &= ~(1 << location); } return offset; } internal static bool IsVectorOrArrayVariable(IoVariable variable) { return variable switch { IoVariable.ClipDistance or IoVariable.Position => true, _ => false, }; } public int GetTfeBufferStorageBufferBinding(int bufferIndex) { return _tfeBufferSbBaseBinding + bufferIndex; } public int GetVertexBufferTextureBinding(int vaLocation) { return _vertexBufferTextureBaseBinding + vaLocation; } public SetBindingPair GetVertexBufferTextureSetAndBinding(int vaLocation) { return new SetBindingPair(TextureSetIndex, GetVertexBufferTextureBinding(vaLocation)); } public SetBindingPair GetIndexBufferTextureSetAndBinding() { return new SetBindingPair(TextureSetIndex, IndexBufferTextureBinding); } public SetBindingPair GetTopologyRemapBufferTextureSetAndBinding() { return new SetBindingPair(TextureSetIndex, TopologyRemapBufferTextureBinding); } internal bool TryGetOffset(StorageKind storageKind, int location, int component, out int offset) { return _offsets.TryGetValue(new IoDefinition(storageKind, IoVariable.UserDefined, location, component), out offset); } internal bool TryGetOffset(StorageKind storageKind, IoVariable ioVariable, int location, int component, out int offset) { return _offsets.TryGetValue(new IoDefinition(storageKind, ioVariable, location, component), out offset); } internal bool TryGetOffset(StorageKind storageKind, IoVariable ioVariable, int component, out int offset) { return _offsets.TryGetValue(new IoDefinition(storageKind, ioVariable, 0, component), out offset); } internal bool TryGetOffset(StorageKind storageKind, IoVariable ioVariable, out int offset) { return _offsets.TryGetValue(new IoDefinition(storageKind, ioVariable, 0, 0), out offset); } } }