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

@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
class AstOperation : AstNode
{
public Instruction Inst { get; }
public StorageKind StorageKind { get; }
public int Index { get; }
@ -16,9 +17,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public int SourcesCount => _sources.Length;
public AstOperation(Instruction inst, IAstNode[] sources, int sourcesCount)
public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount)
{
Inst = inst;
Inst = inst;
StorageKind = storageKind;
_sources = sources;
for (int index = 0; index < sources.Length; index++)
@ -36,12 +38,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Index = 0;
}
public AstOperation(Instruction inst, int index, IAstNode[] sources, int sourcesCount) : this(inst, sources, sourcesCount)
public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount)
{
Index = index;
}
public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, sources, sources.Length)
public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length)
{
}

View file

@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
int cbufSlot,
int handle,
int index,
params IAstNode[] sources) : base(inst, index, sources, sources.Length)
params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length)
{
Type = type;
Format = format;

View file

@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.ImageStore, AggregateType.Void);
Add(Instruction.ImageAtomic, AggregateType.S32);
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
Add(Instruction.LoadAttribute, AggregateType.FP32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
Add(Instruction.Load, AggregateType.FP32);
Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.StoreAttribute, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.FP32);
Add(Instruction.Store, AggregateType.Void);
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
return AggregateType.FP32;
}
else if (inst == Instruction.Call)
else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store)
{
return AggregateType.S32;
}

View file

@ -0,0 +1,44 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System;
namespace Ryujinx.Graphics.Shader.StructuredIr
{
readonly struct IoDefinition : IEquatable<IoDefinition>
{
public StorageKind StorageKind { get; }
public IoVariable IoVariable { get; }
public int Location { get; }
public int Component { get; }
public IoDefinition(StorageKind storageKind, IoVariable ioVariable, int location = 0, int component = 0)
{
StorageKind = storageKind;
IoVariable = ioVariable;
Location = location;
Component = component;
}
public override bool Equals(object other)
{
return other is IoDefinition ioDefinition && Equals(ioDefinition);
}
public bool Equals(IoDefinition other)
{
return StorageKind == other.StorageKind &&
IoVariable == other.IoVariable &&
Location == other.Location &&
Component == other.Component;
}
public override int GetHashCode()
{
return (int)StorageKind | ((int)IoVariable << 8) | (Location << 16) | (Component << 24);
}
public override string ToString()
{
return $"{StorageKind}.{IoVariable}.{Location}.{Component}";
}
}
}

View file

@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return type switch
{
OperandType.Argument => AggregateType.S32,
OperandType.Attribute => AggregateType.FP32,
OperandType.AttributePerPatch => AggregateType.FP32,
OperandType.Constant => AggregateType.S32,
OperandType.ConstantBuffer => AggregateType.FP32,
OperandType.Undefined => AggregateType.S32,

View file

@ -65,49 +65,35 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
context.LeaveFunction();
}
if (config.TransformFeedbackEnabled && (config.LastInVertexPipeline || config.Stage == ShaderStage.Fragment))
{
for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
{
var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
for (int i = 0; i < locations.Length; i++)
{
byte location = locations[i];
if (location < 0xc0)
{
context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
}
}
}
}
return context.Info;
}
private static void AddOperation(StructuredProgramContext context, Operation operation)
{
Instruction inst = operation.Inst;
StorageKind storageKind = operation.StorageKind;
if (inst == Instruction.LoadAttribute)
if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput())
{
Operand src1 = operation.GetSource(0);
Operand src2 = operation.GetSource(1);
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
bool isOutput = storageKind.IsOutput();
bool perPatch = storageKind.IsPerPatch();
int location = 0;
int component = 0;
if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant)
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
{
int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2);
location = operation.GetSource(1).Value;
if ((src1.Value & AttributeConsts.LoadOutputMask) != 0)
if (operation.SourcesCount > 2 &&
operation.GetSource(2).Type == OperandType.Constant &&
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
{
context.Info.Outputs.Add(attrOffset);
}
else
{
context.Info.Inputs.Add(attrOffset);
component = operation.GetSource(2).Value;
}
}
context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
}
bool vectorDest = IsVectorDestInst(inst);
@ -119,12 +105,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
for (int index = 0; index < operation.SourcesCount; index++)
{
sources[index] = context.GetOperandUse(operation.GetSource(index));
sources[index] = context.GetOperand(operation.GetSource(index));
}
for (int index = 0; index < outDestsCount; index++)
{
AstOperand oper = context.GetOperandDef(operation.GetDest(1 + index));
AstOperand oper = context.GetOperand(operation.GetDest(1 + index));
oper.VarType = InstructionInfo.GetSrcVarType(inst, sourcesCount + index);
@ -163,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else
{
source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
}
AggregateType destElemType = destType;
@ -181,17 +167,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
for (int i = 0; i < operation.DestsCount; i++)
{
AstOperand dest = context.GetOperandDef(operation.GetDest(i));
AstOperand dest = context.GetOperand(operation.GetDest(i));
AstOperand index = new AstOperand(OperandType.Constant, i);
dest.VarType = destElemType;
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, new[] { destVec, index }, 2)));
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2)));
}
}
else if (operation.Dest != null)
{
AstOperand dest = context.GetOperandDef(operation.Dest);
AstOperand dest = context.GetOperand(operation.Dest);
// If all the sources are bool, it's better to use short-circuiting
// logical operations, rather than forcing a cast to int and doing
@ -234,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else if (!isCopy)
{
source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
}
else
{
@ -255,7 +241,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else
{
context.AddNode(new AstOperation(inst, operation.Index, sources, operation.SourcesCount));
context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount));
}
// Those instructions needs to be emulated by using helper functions,
@ -263,13 +249,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
// decide which helper functions are needed on the final generated code.
switch (operation.Inst)
{
case Instruction.AtomicMaxS32 | Instruction.MrShared:
case Instruction.AtomicMinS32 | Instruction.MrShared:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
break;
case Instruction.AtomicMaxS32 | Instruction.MrStorage:
case Instruction.AtomicMinS32 | Instruction.MrStorage:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
case Instruction.AtomicMaxS32:
case Instruction.AtomicMinS32:
if (operation.StorageKind == StorageKind.SharedMemory)
{
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
}
else if (operation.StorageKind == StorageKind.StorageBuffer)
{
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
}
break;
case Instruction.MultiplyHighS32:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;

View file

@ -37,43 +37,26 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Config = config;
if (config.Stage == ShaderStage.TessellationControl)
{
// Required to index outputs.
Info.Inputs.Add(AttributeConsts.InvocationId);
}
else if (config.GpPassthrough)
if (config.GpPassthrough)
{
int passthroughAttributes = config.PassthroughAttributes;
while (passthroughAttributes != 0)
{
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
int attrBase = AttributeConsts.UserAttributeBase + index * 16;
Info.Inputs.Add(attrBase);
Info.Inputs.Add(attrBase + 4);
Info.Inputs.Add(attrBase + 8);
Info.Inputs.Add(attrBase + 12);
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.UserDefined, index));
passthroughAttributes &= ~(1 << index);
}
Info.Inputs.Add(AttributeConsts.PositionX);
Info.Inputs.Add(AttributeConsts.PositionY);
Info.Inputs.Add(AttributeConsts.PositionZ);
Info.Inputs.Add(AttributeConsts.PositionW);
Info.Inputs.Add(AttributeConsts.PointSize);
for (int i = 0; i < 8; i++)
{
Info.Inputs.Add(AttributeConsts.ClipDistance0 + i * 4);
}
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.Position));
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.PointSize));
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.ClipDistance));
}
else if (config.Stage == ShaderStage.Fragment)
{
// Potentially used for texture coordinate scaling.
Info.Inputs.Add(AttributeConsts.PositionX);
Info.Inputs.Add(AttributeConsts.PositionY);
Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord));
}
}
@ -281,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else
{
cond = GetOperandUse(branchOp.GetSource(0));
cond = GetOperand(branchOp.GetSource(0));
Instruction invInst = type == AstBlockType.If
? Instruction.BranchIfTrue
@ -315,41 +298,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return newTemp;
}
public AstOperand GetOperandDef(Operand operand)
{
if (operand.Type == OperandType.Attribute)
{
Info.Outputs.Add(operand.Value & AttributeConsts.Mask);
}
else if (operand.Type == OperandType.AttributePerPatch)
{
Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask);
}
return GetOperand(operand);
}
public AstOperand GetOperandUse(Operand operand)
{
// If this flag is set, we're reading from an output attribute instead.
if (operand.Type.IsAttribute() && (operand.Value & AttributeConsts.LoadOutputMask) != 0)
{
return GetOperandDef(operand);
}
if (operand.Type == OperandType.Attribute)
{
Info.Inputs.Add(operand.Value);
}
else if (operand.Type == OperandType.AttributePerPatch)
{
Info.InputsPerPatch.Add(operand.Value);
}
return GetOperand(operand);
}
private AstOperand GetOperand(Operand operand)
public AstOperand GetOperand(Operand operand)
{
if (operand == null)
{

View file

@ -22,60 +22,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
public List<StructuredFunction> Functions { get; }
public HashSet<int> Inputs { get; }
public HashSet<int> Outputs { get; }
public HashSet<int> InputsPerPatch { get; }
public HashSet<int> OutputsPerPatch { get; }
public HashSet<IoDefinition> IoDefinitions { get; }
public HelperFunctionsMask HelperFunctionsMask { get; set; }
public TransformFeedbackOutput[] TransformFeedbackOutputs { get; }
public StructuredProgramInfo()
{
Functions = new List<StructuredFunction>();
Inputs = new HashSet<int>();
Outputs = new HashSet<int>();
InputsPerPatch = new HashSet<int>();
OutputsPerPatch = new HashSet<int>();
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
{
int index = attr / 4;
return TransformFeedbackOutputs[index];
}
public int GetTransformFeedbackOutputComponents(int attr)
{
int index = attr / 4;
int baseIndex = index & ~3;
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;
IoDefinitions = new HashSet<IoDefinition>();
}
}
}