From 95d5a500426e0545247994bbea9f1835bf5dda6f Mon Sep 17 00:00:00 2001 From: gdk Date: Wed, 6 Apr 2022 14:39:21 -0300 Subject: [PATCH] SPIR-V: Implement attribute indexing and StoreAttribute --- .../CodeGen/Spirv/CodeGenContext.cs | 80 +++++++++++++++--- .../CodeGen/Spirv/Declarations.cs | 81 ++++++++++++++----- .../CodeGen/Spirv/Instructions.cs | 37 ++++++++- 3 files changed, 163 insertions(+), 35 deletions(-) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 9820b2591..cdfef6ba0 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -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 Samplers { get; } = new Dictionary(); public Dictionary Images { get; } = new Dictionary(); public Dictionary Inputs { get; } = new Dictionary(); @@ -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 diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 13f852d1d..573c9feeb 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -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); } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 7ceca154d..e10df30d4 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -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);