SPIR-V: Implement attribute indexing and StoreAttribute

This commit is contained in:
gdk 2022-04-06 14:39:21 -03:00 committed by riperiperi
parent 2ef7622126
commit 95d5a50042
3 changed files with 163 additions and 35 deletions

View file

@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction StorageBuffersArray { get; set; }
public Instruction LocalMemory { get; set; }
public Instruction SharedMemory { get; set; }
public Instruction InputsArray { get; set; }
public Instruction OutputsArray { get; set; }
public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>();
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, Instruction>();
@ -144,7 +146,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction[] GetMainInterface()
{
return Inputs.Values.Concat(Outputs.Values).ToArray();
var mainInterface = Inputs.Values.Concat(Outputs.Values);
if (InputsArray != null)
{
mainInterface = mainInterface.Append(InputsArray);
}
if (OutputsArray != null)
{
mainInterface = mainInterface.Append(OutputsArray);
}
return mainInterface.ToArray();
}
public void DeclareLocal(AstOperand local, Instruction spvLocal)
@ -211,23 +225,39 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
throw new NotImplementedException(node.GetType().Name);
}
public Instruction GetAttributeVectorPointer(AstOperand operand, bool isOutAttr)
{
var attrInfo = AttributeInfo.From(Config, operand.Value, isOutAttr);
return isOutAttr ? Outputs[attrInfo.BaseValue] : Inputs[attrInfo.BaseValue];
}
public Instruction GetAttributeElemPointer(int attr, bool isOutAttr, Instruction index, out AggregateType elemType)
{
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
var attrInfo = AttributeInfo.From(Config, attr, isOutAttr);
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
int attrOffset = attrInfo.BaseValue;
AggregateType type = attrInfo.Type;
Instruction ioVariable, elemIndex;
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (isUserAttr &&
((!isOutAttr && Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
(isOutAttr && Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))))
{
elemType = AggregateType.FP32;
ioVariable = isOutAttr ? OutputsArray : InputsArray;
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4);
if (Config.Stage == ShaderStage.Geometry && !isOutAttr)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
}
else
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex);
}
}
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
if (isUserAttr && Config.TransformFeedbackEnabled &&
((isOutAttr && Config.Stage != ShaderStage.Fragment) ||
(!isOutAttr && Config.Stage != ShaderStage.Vertex)))
@ -236,15 +266,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
type = elemType;
}
var ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset];
ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset];
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
{
return ioVariable;
}
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
if (Config.Stage == ShaderStage.Geometry && !isOutAttr && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
{
@ -256,12 +285,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
public Instruction GetAttributeElemPointer(Instruction attrIndex, bool isOutAttr, Instruction index, out AggregateType elemType)
{
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
elemType = AggregateType.FP32;
var ioVariable = isOutAttr ? OutputsArray : InputsArray;
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3));
if (Config.Stage == ShaderStage.Geometry && !isOutAttr)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
}
else
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex);
}
}
public Instruction GetAttribute(AggregateType type, int attr, bool isOutAttr, Instruction index = null)
{
var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, 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);
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
}
public Instruction GetConstant(AggregateType type, AstOperand operand)
{
return type switch

View file

@ -331,41 +331,82 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
{
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
foreach (int attr in info.Inputs)
{
PixelImap iq = PixelImap.Unused;
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (context.Config.Stage == ShaderStage.Fragment &&
attr >= AttributeConsts.UserAttributeBase &&
attr < AttributeConsts.UserAttributeEnd)
if (iaIndexing && isUserAttr)
{
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
}
if (context.InputsArray == null)
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
DeclareInputOrOutput(context, attr, false, iq);
if (context.Config.Stage == ShaderStage.Geometry)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
}
var spvType = context.TypePointer(StorageClass.Input, attrType);
var spvVar = context.Variable(spvType, StorageClass.Input);
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
context.AddGlobalVariable(spvVar);
context.InputsArray = spvVar;
}
}
else
{
PixelImap iq = PixelImap.Unused;
if (context.Config.Stage == ShaderStage.Fragment &&
attr >= AttributeConsts.UserAttributeBase &&
attr < AttributeConsts.UserAttributeEnd)
{
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
}
DeclareInputOrOutput(context, attr, false, iq);
}
}
}
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
{
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
foreach (int attr in info.Outputs)
{
DeclareOutputAttribute(context, attr);
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (oaIndexing && isUserAttr)
{
if (context.OutputsArray == null)
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
var spvType = context.TypePointer(StorageClass.Output, attrType);
var spvVar = context.Variable(spvType, StorageClass.Output);
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
context.AddGlobalVariable(spvVar);
context.OutputsArray = spvVar;
}
}
else
{
DeclareOutputAttribute(context, attr);
}
}
if (context.Config.Stage != ShaderStage.Compute &&
context.Config.Stage != ShaderStage.Fragment &&
!context.Config.GpPassthrough)
if (context.Config.Stage == ShaderStage.Vertex)
{
for (int attr = 0; attr < MaxAttributes; attr++)
{
DeclareOutputAttribute(context, AttributeConsts.UserAttributeBase + attr * 16);
}
if (context.Config.Stage == ShaderStage.Vertex)
{
DeclareOutputAttribute(context, AttributeConsts.PositionX);
}
DeclareOutputAttribute(context, AttributeConsts.PositionX);
}
}

View file

@ -121,6 +121,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.ShuffleXor, GenerateShuffleXor);
Add(Instruction.Sine, GenerateSine);
Add(Instruction.SquareRoot, GenerateSquareRoot);
Add(Instruction.StoreAttribute, GenerateStoreAttribute);
Add(Instruction.StoreLocal, GenerateStoreLocal);
Add(Instruction.StoreShared, GenerateStoreShared);
Add(Instruction.StoreStorage, GenerateStoreStorage);
@ -864,11 +865,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{
int attrOffset = baseAttr.Value + (operand.Value << 2);
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, false, index));
return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr: false, index));
}
else
{
throw new NotImplementedException();
var attr = context.Get(AggregateType.S32, src2);
return new OperationResult(resultType, context.GetAttribute(resultType, attr, isOutAttr: false, index));
}
}
@ -1244,6 +1246,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return GenerateUnary(context, operation, context.Delegates.GlslSqrt, null);
}
private static OperationResult GenerateStoreAttribute(CodeGenContext context, AstOperation operation)
{
var src1 = operation.GetSource(0);
var src2 = operation.GetSource(1);
var src3 = operation.GetSource(2);
if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {nameof(Instruction.StoreAttribute)} must be a constant operand.");
}
SpvInstruction elemPointer;
AggregateType elemType;
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{
int attrOffset = baseAttr.Value + (operand.Value << 2);
elemPointer = context.GetAttributeElemPointer(attrOffset, isOutAttr: true, index: null, out elemType);
}
else
{
var attr = context.Get(AggregateType.S32, src2);
elemPointer = context.GetAttributeElemPointer(attr, isOutAttr: true, index: null, out elemType);
}
var value = context.Get(elemType, src3);
context.Store(elemPointer, value);
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation)
{
return GenerateStoreLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);