Refactor attribute handling on the shader generator (#4565)

* Refactor attribute handling on the shader generator

* Implement gl_ViewportMask[]

* Add back the Intel FrontFacing bug workaround

* Fix GLSL transform feedback outputs mistmatch with fragment stage

* Shader cache version bump

* Fix geometry shader recognition

* PR feedback

* Delete GetOperandDef and GetOperandUse

* Remove replacements that are no longer needed on GLSL compilation on Vulkan

* Fix incorrect load for per-patch outputs

* Fix build
This commit is contained in:
gdkchan 2023-04-25 19:51:07 -03:00 committed by GitHub
parent 097562bc6c
commit 9f12e50a54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 1967 additions and 1746 deletions

View file

@ -41,6 +41,46 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool TransformFeedbackEnabled { get; }
private TransformFeedbackOutput[] _transformFeedbackOutputs;
readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable>
{
public IoVariable IoVariable { get; }
public int Location { get; }
public int Component { get; }
public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0)
{
IoVariable = ioVariable;
Location = location;
Component = component;
}
public override bool Equals(object other)
{
return other is TransformFeedbackVariable tfbVar && Equals(tfbVar);
}
public bool Equals(TransformFeedbackVariable other)
{
return IoVariable == other.IoVariable &&
Location == other.Location &&
Component == other.Component;
}
public override int GetHashCode()
{
return (int)IoVariable | (Location << 8) | (Component << 16);
}
public override string ToString()
{
return $"{IoVariable}.{Location}.{Component}";
}
}
private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions;
public int Size { get; private set; }
public byte ClipDistancesWritten { get; private set; }
@ -102,6 +142,8 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor = gpuAccessor;
Options = options;
_transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1;
AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1;
@ -147,6 +189,173 @@ namespace Ryujinx.Graphics.Shader.Translation
LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
}
private void EnsureTransformFeedbackInitialized()
{
if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null)
{
TransformFeedbackOutput[] transformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
ulong vecMap = 0UL;
for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
{
var locations = GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
var stride = GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
for (int i = 0; i < locations.Length; i++)
{
byte wordOffset = locations[i];
if (wordOffset < 0xc0)
{
transformFeedbackOutputs[wordOffset] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
vecMap |= 1UL << (wordOffset / 4);
}
}
}
_transformFeedbackOutputs = transformFeedbackOutputs;
while (vecMap != 0)
{
int vecIndex = BitOperations.TrailingZeroCount(vecMap);
for (int subIndex = 0; subIndex < 4; subIndex++)
{
int wordOffset = vecIndex * 4 + subIndex;
int byteOffset = wordOffset * 4;
if (transformFeedbackOutputs[wordOffset].Valid)
{
IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location);
int component = 0;
if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true))
{
component = subIndex;
}
var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
_transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]);
}
}
vecMap &= ~(1UL << vecIndex);
}
}
}
public TransformFeedbackOutput[] GetTransformFeedbackOutputs()
{
EnsureTransformFeedbackInitialized();
return _transformFeedbackOutputs;
}
public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput)
{
EnsureTransformFeedbackInitialized();
var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput);
}
private bool HasTransformFeedbackOutputs()
{
return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment);
}
public bool HasTransformFeedbackOutputs(bool isOutput)
{
return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment));
}
public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput)
{
if (ioVariable == IoVariable.UserDefined)
{
return (!isOutput && !UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
(isOutput && !UsedFeatures.HasFlag(FeatureFlags.OaIndexing));
}
return ioVariable == IoVariable.FragmentOutputColor;
}
public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput)
{
if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput))
{
return false;
}
return GetTransformFeedbackOutputComponents(location, component) == 1;
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset)
{
EnsureTransformFeedbackInitialized();
return _transformFeedbackOutputs[wordOffset];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
{
return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component);
}
public int GetTransformFeedbackOutputComponents(int location, int component)
{
EnsureTransformFeedbackInitialized();
int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4;
int index = baseIndex + component;
int count = 1;
for (; count < 4; count++)
{
ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1];
ref var curr = ref _transformFeedbackOutputs[baseIndex + count];
int prevOffset = prev.Offset;
int currOffset = curr.Offset;
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
{
break;
}
}
if (baseIndex + count <= index)
{
return 1;
}
return count;
}
public AggregateType GetFragmentOutputColorType(int location)
{
return AggregateType.Vector4 | GpuAccessor.QueryFragmentOutputType(location).ToAggregateType();
}
public AggregateType GetUserDefinedType(int location, bool isOutput)
{
if ((!isOutput && UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
(isOutput && UsedFeatures.HasFlag(FeatureFlags.OaIndexing)))
{
return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32;
}
AggregateType type = AggregateType.Vector4;
if (Stage == ShaderStage.Vertex && !isOutput)
{
type |= GpuAccessor.QueryAttributeType(location).ToAggregateType();
}
else
{
type |= AggregateType.FP32;
}
return type;
}
public int GetDepthRegister()
{
// The depth register is always two registers after the last color output.
@ -184,7 +393,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return format;
}
private bool FormatSupportsAtomic(TextureFormat format)
private static bool FormatSupportsAtomic(TextureFormat format)
{
return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint;
}