Ryujinx/src/Ryujinx.Graphics.Shader/Translation/ResourceReservations.cs
gdkchan f09bba82b9
Geometry shader emulation for macOS (#5551)
* Implement vertex and geometry shader conversion to compute

* Call InitializeReservedCounts for compute too

* PR feedback

* Set clip distance mask for geometry and tessellation shaders too

* Transform feedback emulation only for vertex
2023-08-29 21:10:34 -03:00

186 lines
7 KiB
C#

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;
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<IoDefinition, int> _offsets;
internal IReadOnlyDictionary<IoDefinition, int> 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;
}
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);
}
}
}