mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-07-25 07:57:11 +02:00
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:
parent
097562bc6c
commit
9f12e50a54
56 changed files with 1967 additions and 1746 deletions
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
44
Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs
Normal file
44
Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue