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<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, 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> 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<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>(); private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
@ -148,7 +150,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction[] GetMainInterface() 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) if (InputsArray != null)
{ {
@ -216,6 +221,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
IrOperandType.Argument => GetArgument(type, operand), IrOperandType.Argument => GetArgument(type, operand),
IrOperandType.Attribute => GetAttribute(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0), 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.Constant => GetConstant(type, operand),
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand), IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
IrOperandType.LocalVariable => GetLocal(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)); 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) public Instruction GetAttribute(AggregateType type, Instruction attr, bool isOutAttr, Instruction index = null)
{ {
var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType); 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()); DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareSamplers(context, context.Config.GetTextureDescriptors());
DeclareImages(context, context.Config.GetImageDescriptors()); DeclareImages(context, context.Config.GetImageDescriptors());
DeclareInputAttributes(context, info); DeclareInputAttributes(context, info, perPatch: false);
DeclareOutputAttributes(context, info); DeclareOutputAttributes(context, info, perPatch: false);
DeclareInputAttributes(context, info, perPatch: true);
DeclareOutputAttributes(context, info, perPatch: true);
} }
private static void DeclareLocalMemory(CodeGenContext context, int size) 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); 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)) 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; bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (iaIndexing && isUserAttr) if (iaIndexing && isUserAttr && !perPatch)
{ {
if (context.InputsArray == null) if (context.InputsArray == null)
{ {
@ -399,16 +402,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); 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); 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)) 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; bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (oaIndexing && isUserAttr) if (oaIndexing && isUserAttr && !perPatch)
{ {
if (context.OutputsArray == null) if (context.OutputsArray == null)
{ {
@ -435,30 +439,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
else else
{ {
DeclareOutputAttribute(context, attr); DeclareOutputAttribute(context, attr, perPatch);
} }
} }
if (context.Config.Stage == ShaderStage.Vertex) 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) 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; 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.Fragment) ||
(!isOutAttr && context.Config.Stage != ShaderStage.Vertex))) (!isOutAttr && context.Config.Stage != ShaderStage.Vertex)))
{ {
@ -466,7 +470,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return; 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); var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
if (dict.ContainsKey(attrInfo.BaseValue)) if (dict.ContainsKey(attrInfo.BaseValue))
@ -485,6 +492,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var spvType = context.TypePointer(storageClass, attrType); var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass); var spvVar = context.Variable(spvType, storageClass);
if (perPatch)
{
context.Decorate(spvVar, Decoration.Patch);
}
if (attrInfo.IsBuiltin) if (attrInfo.IsBuiltin)
{ {
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
@ -503,6 +515,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
else if (isUserAttr) else if (isUserAttr)
{ {
int location = (attr - AttributeConsts.UserAttributeBase) / 16; int location = (attr - AttributeConsts.UserAttributeBase) / 16;
if (perPatch)
{
location += 32;
}
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
if (!isOutAttr) if (!isOutAttr)
@ -586,6 +604,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
return attr switch return attr switch
{ {
AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
AttributeConsts.Layer => BuiltIn.Layer, AttributeConsts.Layer => BuiltIn.Layer,
AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex, AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
AttributeConsts.PointSize => BuiltIn.PointSize, AttributeConsts.PointSize => BuiltIn.PointSize,

View file

@ -66,11 +66,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
context.AddExtension("SPV_EXT_fragment_shader_interlock"); context.AddExtension("SPV_EXT_fragment_shader_interlock");
} }
else if (config.Stage == ShaderStage.Geometry)
if (config.Stage == ShaderStage.Geometry)
{ {
context.AddCapability(Capability.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_shader_ballot");
context.AddExtension("SPV_KHR_subgroup_vote"); 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()); 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: case InputTopology.Points:
context.AddExecutionMode(spvFunc, ExecutionMode.InputPoints); context.AddExecutionMode(spvFunc, ExecutionMode.InputPoints);
@ -329,11 +371,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var source = context.Get(dest.VarType.Convert(), assignment.Source); var source = context.Get(dest.VarType.Convert(), assignment.Source);
context.Store(context.GetLocalPointer(dest), 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)) 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)); 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); Info.Outputs.Add(operand.Value & AttributeConsts.Mask);
} }
else if (operand.Type == OperandType.AttributePerPatch)
{
Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask);
}
return GetOperand(operand); return GetOperand(operand);
} }
@ -297,6 +301,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
Info.Inputs.Add(operand.Value); Info.Inputs.Add(operand.Value);
} }
else if (operand.Type == OperandType.AttributePerPatch)
{
Info.InputsPerPatch.Add(operand.Value);
}
return GetOperand(operand); return GetOperand(operand);
} }

View file

@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public HashSet<int> Inputs { get; } public HashSet<int> Inputs { get; }
public HashSet<int> Outputs { get; } public HashSet<int> Outputs { get; }
public HashSet<int> InputsPerPatch { get; }
public HashSet<int> OutputsPerPatch { get; }
public HelperFunctionsMask HelperFunctionsMask { get; set; } public HelperFunctionsMask HelperFunctionsMask { get; set; }
@ -35,6 +37,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Inputs = new HashSet<int>(); Inputs = new HashSet<int>();
Outputs = new HashSet<int>(); Outputs = new HashSet<int>();
InputsPerPatch = new HashSet<int>();
OutputsPerPatch = new HashSet<int>();
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; 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>() private static readonly Dictionary<int, AttributeInfo> BuiltInAttributes = new Dictionary<int, AttributeInfo>()
{ {
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) }, { AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) }, { AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) }, { AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) }, { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, { 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. // Special.
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) }, { AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },