SPIR-V: Implement tessellation support

This commit is contained in:
gdk 2022-04-07 16:45:22 -03:00 committed by riperiperi
parent 070996ad6d
commit cacc9c7da4
6 changed files with 173 additions and 47 deletions

View file

@ -31,6 +31,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, Instruction>();
public Dictionary<int, Instruction> Outputs { get; } = new Dictionary<int, Instruction>();
public Dictionary<int, Instruction> InputsPerPatch { get; } = new Dictionary<int, Instruction>();
public Dictionary<int, Instruction> OutputsPerPatch { get; } = new Dictionary<int, Instruction>();
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
@ -148,7 +150,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction[] GetMainInterface()
{
var mainInterface = Inputs.Values.Concat(Outputs.Values);
var mainInterface = Inputs.Values
.Concat(Outputs.Values)
.Concat(InputsPerPatch.Values)
.Concat(OutputsPerPatch.Values);
if (InputsArray != null)
{
@ -216,6 +221,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
IrOperandType.Argument => GetArgument(type, operand),
IrOperandType.Attribute => GetAttribute(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0),
IrOperandType.AttributePerPatch => GetAttributePerPatch(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0),
IrOperandType.Constant => GetConstant(type, operand),
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
IrOperandType.LocalVariable => GetLocal(type, operand),
@ -317,6 +323,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
}
public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)
{
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
var attrInfo = AttributeInfo.From(Config, attr, isOutAttr);
int attrOffset = attrInfo.BaseValue;
Instruction ioVariable;
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
{
return ioVariable;
}
var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex);
}
public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr)
{
if (!AttributeInfo.Validate(Config, attr, isOutAttr: false))
{
return GetConstant(type, new AstOperand(IrOperandType.Constant, 0));
}
var elemPointer = GetAttributePerPatchElemPointer(attr, isOutAttr, out var elemType);
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
}
public Instruction GetAttribute(AggregateType type, Instruction attr, bool isOutAttr, Instruction index = null)
{
var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType);

View file

@ -98,8 +98,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
DeclareSamplers(context, context.Config.GetTextureDescriptors());
DeclareImages(context, context.Config.GetImageDescriptors());
DeclareInputAttributes(context, info);
DeclareOutputAttributes(context, info);
DeclareInputAttributes(context, info, perPatch: false);
DeclareOutputAttributes(context, info, perPatch: false);
DeclareInputAttributes(context, info, perPatch: true);
DeclareOutputAttributes(context, info, perPatch: true);
}
private static void DeclareLocalMemory(CodeGenContext context, int size)
@ -354,11 +356,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
};
}
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
{
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
foreach (int attr in info.Inputs)
foreach (int attr in inputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false))
{
@ -367,7 +370,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (iaIndexing && isUserAttr)
if (iaIndexing && isUserAttr && !perPatch)
{
if (context.InputsArray == null)
{
@ -399,16 +402,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
}
DeclareInputOrOutput(context, attr, false, iq);
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
}
}
}
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
{
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
foreach (int attr in info.Outputs)
foreach (int attr in outputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true))
{
@ -417,7 +421,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (oaIndexing && isUserAttr)
if (oaIndexing && isUserAttr && !perPatch)
{
if (context.OutputsArray == null)
{
@ -435,30 +439,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
else
{
DeclareOutputAttribute(context, attr);
DeclareOutputAttribute(context, attr, perPatch);
}
}
if (context.Config.Stage == ShaderStage.Vertex)
{
DeclareOutputAttribute(context, AttributeConsts.PositionX);
DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false);
}
}
private static void DeclareOutputAttribute(CodeGenContext context, int attr)
private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch)
{
DeclareInputOrOutput(context, attr, true);
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true);
}
public static void DeclareInvocationId(CodeGenContext context)
{
DeclareInputOrOutput(context, AttributeConsts.LaneId, false);
DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false);
}
private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused)
{
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (isUserAttr && context.Config.TransformFeedbackEnabled &&
if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch &&
((isOutAttr && context.Config.Stage != ShaderStage.Fragment) ||
(!isOutAttr && context.Config.Stage != ShaderStage.Vertex)))
{
@ -466,7 +470,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return;
}
var dict = isOutAttr ? context.Outputs : context.Inputs;
var dict = perPatch
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
: (isOutAttr ? context.Outputs : context.Inputs);
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
if (dict.ContainsKey(attrInfo.BaseValue))
@ -485,6 +492,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass);
if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
}
if (attrInfo.IsBuiltin)
{
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
@ -503,6 +515,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
else if (isUserAttr)
{
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
if (perPatch)
{
location += 32;
}
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
if (!isOutAttr)
@ -586,6 +604,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
return attr switch
{
AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
AttributeConsts.Layer => BuiltIn.Layer,
AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
AttributeConsts.PointSize => BuiltIn.PointSize,

View file

@ -66,11 +66,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
context.AddExtension("SPV_EXT_fragment_shader_interlock");
}
if (config.Stage == ShaderStage.Geometry)
else if (config.Stage == ShaderStage.Geometry)
{
context.AddCapability(Capability.Geometry);
}
else if (config.Stage == ShaderStage.TessellationControl || config.Stage == ShaderStage.TessellationEvaluation)
{
context.AddCapability(Capability.Tessellation);
}
context.AddExtension("SPV_KHR_shader_ballot");
context.AddExtension("SPV_KHR_subgroup_vote");
@ -149,11 +152,50 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface());
if (context.Config.Stage == ShaderStage.Geometry)
if (context.Config.Stage == ShaderStage.TessellationControl)
{
InputTopology inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology();
context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive);
}
else if (context.Config.Stage == ShaderStage.TessellationEvaluation)
{
switch (context.Config.GpuAccessor.QueryTessPatchType())
{
case TessPatchType.Isolines:
context.AddExecutionMode(spvFunc, ExecutionMode.Isolines);
break;
case TessPatchType.Triangles:
context.AddExecutionMode(spvFunc, ExecutionMode.Triangles);
break;
case TessPatchType.Quads:
context.AddExecutionMode(spvFunc, ExecutionMode.Quads);
break;
}
switch (inPrimitive)
switch (context.Config.GpuAccessor.QueryTessSpacing())
{
case TessSpacing.EqualSpacing:
context.AddExecutionMode(spvFunc, ExecutionMode.SpacingEqual);
break;
case TessSpacing.FractionalEventSpacing:
context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalEven);
break;
case TessSpacing.FractionalOddSpacing:
context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalOdd);
break;
}
if (context.Config.GpuAccessor.QueryTessCw())
{
context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw);
}
else
{
context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCcw);
}
}
else if (context.Config.Stage == ShaderStage.Geometry)
{
switch (context.Config.GpuAccessor.QueryPrimitiveTopology())
{
case InputTopology.Points:
context.AddExecutionMode(spvFunc, ExecutionMode.InputPoints);
@ -329,11 +371,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var source = context.Get(dest.VarType.Convert(), assignment.Source);
context.Store(context.GetLocalPointer(dest), source);
}
else if (dest.Type == OperandType.Attribute)
else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch)
{
if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true))
{
var elemPointer = context.GetAttributeElemPointer(dest.Value, true, null, out var elemType);
bool perPatch = dest.Type == OperandType.AttributePerPatch;
AggregateType elemType;
var elemPointer = perPatch
? context.GetAttributePerPatchElemPointer(dest.Value, true, out elemType)
: context.GetAttributeElemPointer(dest.Value, true, null, out elemType);
context.Store(elemPointer, context.Get(elemType, assignment.Source));
}
}

View file

@ -281,6 +281,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
Info.Outputs.Add(operand.Value & AttributeConsts.Mask);
}
else if (operand.Type == OperandType.AttributePerPatch)
{
Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask);
}
return GetOperand(operand);
}
@ -297,6 +301,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
Info.Inputs.Add(operand.Value);
}
else if (operand.Type == OperandType.AttributePerPatch)
{
Info.InputsPerPatch.Add(operand.Value);
}
return GetOperand(operand);
}

View file

@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public HashSet<int> Inputs { get; }
public HashSet<int> Outputs { get; }
public HashSet<int> InputsPerPatch { get; }
public HashSet<int> OutputsPerPatch { get; }
public HelperFunctionsMask HelperFunctionsMask { get; set; }
@ -35,6 +37,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Inputs = new HashSet<int>();
Outputs = new HashSet<int>();
InputsPerPatch = new HashSet<int>();
OutputsPerPatch = new HashSet<int>();
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
}

View file

@ -6,28 +6,34 @@ namespace Ryujinx.Graphics.Shader.Translation
{
private static readonly Dictionary<int, AttributeInfo> BuiltInAttributes = new Dictionary<int, AttributeInfo>()
{
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
// Special.
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },