diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2023-04-25 19:51:07 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-25 19:51:07 -0300 |
| commit | 9f12e50a546b15533778ed0d8290202af91c10a2 (patch) | |
| tree | f0e77a7b7c605face5ef29270b4248af2682301a /Ryujinx.Graphics.Shader/CodeGen/Spirv | |
| parent | 097562bc6c227c42f803ce1078fcb4adf06cd20c (diff) | |
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
Diffstat (limited to 'Ryujinx.Graphics.Shader/CodeGen/Spirv')
7 files changed, 372 insertions, 626 deletions
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index e693307d..ed292ef1 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -29,15 +29,13 @@ 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, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>(); 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>(); - 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>(); + public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); public Instruction CoordTemp { get; set; } private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>(); @@ -163,16 +161,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv mainInterface.AddRange(InputsPerPatch.Values); mainInterface.AddRange(OutputsPerPatch.Values); - if (InputsArray != null) - { - mainInterface.Add(InputsArray); - } - - if (OutputsArray != null) - { - mainInterface.Add(OutputsArray); - } - return mainInterface.ToArray(); } @@ -228,8 +216,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return operand.Type switch { 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), @@ -275,239 +261,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv }; } - 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); - - int attrOffset = attrInfo.BaseValue; - AggregateType type = attrInfo.Type; - - Instruction ioVariable, elemIndex; - - Instruction invocationId = null; - - if (Config.Stage == ShaderStage.TessellationControl && isOutAttr) - { - invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]); - } - - 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); - - bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr); - - if (invocationId != null && isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex); - } - else if (invocationId != null) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex); - } - else if (isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); - } - else - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex); - } - } - - bool isViewportInverse = attr == AttributeConsts.SupportBlockViewInverseX || attr == AttributeConsts.SupportBlockViewInverseY; - - if (isViewportInverse) - { - elemType = AggregateType.FP32; - elemIndex = Constant(TypeU32(), (attr - AttributeConsts.SupportBlockViewInverseX) >> 2); - return AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), SupportBuffer, Constant(TypeU32(), 2), elemIndex); - } - - elemType = attrInfo.Type & AggregateType.ElementTypeMask; - - if (isUserAttr && Config.TransformFeedbackEnabled && - ((isOutAttr && Config.LastInVertexPipeline) || - (!isOutAttr && Config.Stage == ShaderStage.Fragment))) - { - attrOffset = attr; - type = elemType; - - if (isOutAttr) - { - int components = Info.GetTransformFeedbackOutputComponents(attr); - - if (components > 1) - { - attrOffset &= ~0xf; - type = components switch - { - 2 => AggregateType.Vector2 | AggregateType.FP32, - 3 => AggregateType.Vector3 | AggregateType.FP32, - 4 => AggregateType.Vector4 | AggregateType.FP32, - _ => AggregateType.FP32 - }; - - attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false); - } - } - } - - ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset]; - - bool isIndexed = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)); - - if ((type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0) - { - if (invocationId != null) - { - return isIndexed - ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index) - : AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId); - } - else - { - return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable; - } - } - - elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); - - if (invocationId != null && isIndexed) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex); - } - else if (invocationId != null) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex); - } - else if (isIndexed) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex); - } - else - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex); - } - } - - public Instruction GetAttributeElemPointer(Instruction attrIndex, bool isOutAttr, Instruction index, out AggregateType elemType) - { - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - - Instruction invocationId = null; - - if (Config.Stage == ShaderStage.TessellationControl && isOutAttr) - { - invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]); - } - - elemType = AggregateType.FP32; - var ioVariable = isOutAttr ? OutputsArray : InputsArray; - var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2)); - var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3)); - - bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr); - - if (invocationId != null && isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex); - } - else if (invocationId != null) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex); - } - else if (isArray) - { - 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) - { - if (!AttributeInfo.Validate(Config, attr, isOutAttr: false)) - { - return GetConstant(type, new AstOperand(IrOperandType.Constant, 0)); - } - - var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType); - var value = Load(GetType(elemType), elemPointer); - - if (Config.Stage == ShaderStage.Fragment) - { - if (attr == AttributeConsts.PositionX || attr == AttributeConsts.PositionY) - { - var pointerType = TypePointer(StorageClass.Uniform, TypeFP32()); - var fieldIndex = Constant(TypeU32(), 4); - var scaleIndex = Constant(TypeU32(), 0); - - var scaleElemPointer = AccessChain(pointerType, SupportBuffer, fieldIndex, scaleIndex); - var scale = Load(TypeFP32(), scaleElemPointer); - - value = FDiv(TypeFP32(), value, scale); - } - else if (attr == AttributeConsts.FrontFacing && Config.GpuAccessor.QueryHostHasFrontFacingBug()) - { - // Workaround for what appears to be a bug on Intel compiler. - var valueFloat = Select(TypeFP32(), value, Constant(TypeFP32(), 1f), Constant(TypeFP32(), 0f)); - var valueAsInt = Bitcast(TypeS32(), valueFloat); - var valueNegated = SNegate(TypeS32(), valueAsInt); - - value = SLessThan(TypeBool(), valueNegated, Constant(TypeS32(), 0)); - } - } - - return BitcastIfNeeded(type, elemType, value); - } - - public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType) - { - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr); - - int attrOffset = attrInfo.BaseValue; - Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset]; - - elemType = attrInfo.Type & AggregateType.ElementTypeMask; - - if ((attrInfo.Type & (AggregateType.Array | AggregateType.ElementCountMask)) == 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.ValidatePerPatch(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); - 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 fdca5e89..821da477 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -1,21 +1,19 @@ using Ryujinx.Common; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using Spv.Generator; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Numerics; using static Spv.Specification; +using SpvInstruction = Spv.Generator.Instruction; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { static class Declarations { - // At least 16 attributes are guaranteed by the spec. - public const int MaxAttributes = 16; - private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; public static void DeclareParameters(CodeGenContext context, StructuredFunction function) @@ -59,7 +57,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { StructuredFunction function = functions[funcIndex]; - Instruction[] locals = new Instruction[function.InArguments.Length]; + SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length]; for (int i = 0; i < function.InArguments.Length; i++) { @@ -105,10 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors()); DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareImages(context, context.Config.GetImageDescriptors()); - DeclareInputAttributes(context, info, perPatch: false); - DeclareOutputAttributes(context, info, perPatch: false); - DeclareInputAttributes(context, info, perPatch: true); - DeclareOutputAttributes(context, info, perPatch: true); + DeclareInputsAndOutputs(context, info); } private static void DeclareLocalMemory(CodeGenContext context, int size) @@ -121,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size); } - private static Instruction DeclareMemory(CodeGenContext context, StorageClass storage, int size) + private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size) { var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size)); var pointerType = context.TypePointer(storage, arrayType); @@ -395,164 +390,104 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv }; } - private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) + private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info) { - bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing); - - if (iaIndexing && !perPatch) + foreach (var ioDefinition in info.IoDefinitions) { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); - - 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); - - if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) - { - context.Decorate(spvVar, Decoration.PassthroughNV); - } - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.InputsArray = spvVar; - } - - var inputs = perPatch ? info.InputsPerPatch : info.Inputs; + var ioVariable = ioDefinition.IoVariable; - foreach (int attr in inputs) - { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch)) + // Those are actually from constant buffer, rather than being actual inputs or outputs, + // so we must ignore them here as they are declared as part of the support buffer. + // TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input"). + if (ioVariable == IoVariable.FragmentOutputIsBgra || + ioVariable == IoVariable.SupportBlockRenderScale || + ioVariable == IoVariable.SupportBlockViewInverse) { continue; } - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; - - if (iaIndexing && isUserAttr && !perPatch) - { - continue; - } + bool isOutput = ioDefinition.StorageKind.IsOutput(); + bool isPerPatch = ioDefinition.StorageKind.IsPerPatch(); PixelImap iq = PixelImap.Unused; if (context.Config.Stage == ShaderStage.Fragment) { - if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) + if (ioVariable == IoVariable.UserDefined) { - iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); + iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType(); } else { - AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false); - AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask; + (_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable); + AggregateType elemType = varType & AggregateType.ElementTypeMask; - if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32)) + if (elemType == AggregateType.S32 || elemType == AggregateType.U32) { iq = PixelImap.Constant; } } } - DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); + DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq); } } - private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) + private static void DeclareInputOrOutput(CodeGenContext context, IoDefinition ioDefinition, bool isOutput, bool isPerPatch, PixelImap iq = PixelImap.Unused) { - bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing); - - if (oaIndexing && !perPatch) - { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); - - if (context.Config.Stage == ShaderStage.TessellationControl) - { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); - } + IoVariable ioVariable = ioDefinition.IoVariable; + var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; - var spvType = context.TypePointer(StorageClass.Output, attrType); - var spvVar = context.Variable(spvType, StorageClass.Output); + bool isBuiltIn; + BuiltIn builtIn = default; + AggregateType varType; - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.OutputsArray = spvVar; + if (ioVariable == IoVariable.UserDefined) + { + varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput); + isBuiltIn = false; } - - var outputs = perPatch ? info.OutputsPerPatch : info.Outputs; - - foreach (int attr in outputs) + else if (ioVariable == IoVariable.FragmentOutputColor) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch)) - { - continue; - } - - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; + varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location); + isBuiltIn = false; + } + else + { + (builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable); + isBuiltIn = true; - if (oaIndexing && isUserAttr && !perPatch) + if (varType == AggregateType.Invalid) { - continue; + throw new InvalidOperationException($"Unknown variable {ioVariable}."); } - - DeclareOutputAttribute(context, attr, perPatch); } - if (context.Config.Stage == ShaderStage.Vertex) - { - DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false); - } - } - - private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch) - { - DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true); - } - - public static void DeclareInvocationId(CodeGenContext context) - { - DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false); - } + bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput); - 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 && !perPatch && - ((isOutAttr && context.Config.LastInVertexPipeline) || - (!isOutAttr && context.Config.Stage == ShaderStage.Fragment))) + if (hasComponent) { - DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq); - return; + varType &= AggregateType.ElementTypeMask; } - - var dict = perPatch - ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch) - : (isOutAttr ? context.Outputs : context.Inputs); - - var attrInfo = perPatch - ? AttributeInfo.FromPatch(context.Config, attr, isOutAttr) - : AttributeInfo.From(context.Config, attr, isOutAttr); - - if (dict.ContainsKey(attrInfo.BaseValue)) + else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput)) { - return; + varType &= AggregateType.ElementTypeMask; + varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch + { + 2 => AggregateType.Vector2, + 3 => AggregateType.Vector3, + 4 => AggregateType.Vector4, + _ => AggregateType.Invalid + }; } - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrType = context.GetType(attrInfo.Type, attrInfo.Length); + var spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable)); bool builtInPassthrough = false; - if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && !perPatch && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) + if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput)) { int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32; - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); + spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { @@ -560,69 +495,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch) + if (context.Config.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch) { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); } - var spvType = context.TypePointer(storageClass, attrType); - var spvVar = context.Variable(spvType, storageClass); + var spvPointerType = context.TypePointer(storageClass, spvType); + var spvVar = context.Variable(spvPointerType, storageClass); if (builtInPassthrough) { context.Decorate(spvVar, Decoration.PassthroughNV); } - if (attrInfo.IsBuiltin) + if (isBuiltIn) { - if (perPatch) + if (isPerPatch) { context.Decorate(spvVar, Decoration.Patch); } - if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment) + if (context.Config.GpuAccessor.QueryHostReducedPrecision() && ioVariable == IoVariable.Position) { context.Decorate(spvVar, Decoration.Invariant); } - context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); - - if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) - { - var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue); - if (tfOutput.Valid) - { - context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); - context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride); - context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset); - } - } + context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)builtIn); } - else if (perPatch) + else if (isPerPatch) { context.Decorate(spvVar, Decoration.Patch); - int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16); + if (ioVariable == IoVariable.UserDefined) + { + int location = context.Config.GetPerPatchAttributeLocation(ioDefinition.Location); - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } } - else if (isUserAttr) + else if (ioVariable == IoVariable.UserDefined) { - int location = (attr - AttributeConsts.UserAttributeBase) / 16; + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)ioDefinition.Location); - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + if (hasComponent) + { + context.Decorate(spvVar, Decoration.Component, (LiteralInteger)ioDefinition.Component); + } - if (!isOutAttr && - !perPatch && - (context.Config.PassthroughAttributes & (1 << location)) != 0 && + if (!isOutput && + !isPerPatch && + (context.Config.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { context.Decorate(spvVar, Decoration.PassthroughNV); } } - else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) + else if (ioVariable == IoVariable.FragmentOutputColor) { - int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16; + int location = ioDefinition.Location; if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) { @@ -646,7 +576,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - if (!isOutAttr) + if (!isOutput) { switch (iq) { @@ -658,143 +588,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } } - - context.AddGlobalVariable(spvVar); - dict.Add(attrInfo.BaseValue, spvVar); - } - - private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused) - { - var dict = isOutAttr ? context.Outputs : context.Inputs; - var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); - - bool hasComponent = true; - int component = (attr >> 2) & 3; - int components = 1; - var type = attrInfo.Type & AggregateType.ElementTypeMask; - - if (isOutAttr) + else if (context.Config.TryGetTransformFeedbackOutput( + ioVariable, + ioDefinition.Location, + ioDefinition.Component, + out var transformFeedbackOutput)) { - components = context.Info.GetTransformFeedbackOutputComponents(attr); - - if (components > 1) - { - attr &= ~0xf; - type = components switch - { - 2 => AggregateType.Vector2 | AggregateType.FP32, - 3 => AggregateType.Vector3 | AggregateType.FP32, - 4 => AggregateType.Vector4 | AggregateType.FP32, - _ => AggregateType.FP32 - }; - - hasComponent = false; - } - } - - if (dict.ContainsKey(attr)) - { - return; - } - - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrType = context.GetType(type, components); - - if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) - { - int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32; - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); - } - - if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr) - { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); - } - - var spvType = context.TypePointer(storageClass, attrType); - var spvVar = context.Variable(spvType, storageClass); - - Debug.Assert(attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd); - int location = (attr - AttributeConsts.UserAttributeBase) / 16; - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); - - if (hasComponent) - { - context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component); - } - - if (isOutAttr) - { - var tfOutput = context.Info.GetTransformFeedbackOutput(attr); - if (tfOutput.Valid) - { - context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); - context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride); - context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset); - } - } - else - { - if ((context.Config.PassthroughAttributes & (1 << location)) != 0 && - context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) - { - context.Decorate(spvVar, Decoration.PassthroughNV); - } - - switch (iq) - { - case PixelImap.Constant: - context.Decorate(spvVar, Decoration.Flat); - break; - case PixelImap.ScreenLinear: - context.Decorate(spvVar, Decoration.NoPerspective); - break; - } + context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer); + context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride); + context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset); } context.AddGlobalVariable(spvVar); - dict.Add(attr, spvVar); - } - private static BuiltIn GetBuiltIn(CodeGenContext context, int attr) - { - return attr switch - { - AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter, - AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner, - AttributeConsts.Layer => BuiltIn.Layer, - AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex, - AttributeConsts.PointSize => BuiltIn.PointSize, - AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position, - AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance, - AttributeConsts.PointCoordX => BuiltIn.PointCoord, - AttributeConsts.TessCoordX => BuiltIn.TessCoord, - AttributeConsts.InstanceId => BuiltIn.InstanceId, - AttributeConsts.VertexId => BuiltIn.VertexId, - AttributeConsts.BaseInstance => BuiltIn.BaseInstance, - AttributeConsts.BaseVertex => BuiltIn.BaseVertex, - AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex, - AttributeConsts.VertexIndex => BuiltIn.VertexIndex, - AttributeConsts.DrawIndex => BuiltIn.DrawIndex, - AttributeConsts.FrontFacing => BuiltIn.FrontFacing, - AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth, - AttributeConsts.ThreadKill => BuiltIn.HelperInvocation, - AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId, - AttributeConsts.CtaIdX => BuiltIn.WorkgroupId, - AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId, - AttributeConsts.InvocationId => BuiltIn.InvocationId, - AttributeConsts.PrimitiveId => BuiltIn.PrimitiveId, - AttributeConsts.PatchVerticesIn => BuiltIn.PatchVertices, - AttributeConsts.EqMask => BuiltIn.SubgroupEqMask, - AttributeConsts.GeMask => BuiltIn.SubgroupGeMask, - AttributeConsts.GtMask => BuiltIn.SubgroupGtMask, - AttributeConsts.LeMask => BuiltIn.SubgroupLeMask, - AttributeConsts.LtMask => BuiltIn.SubgroupLtMask, - AttributeConsts.SupportBlockViewInverseX => BuiltIn.Position, - AttributeConsts.SupportBlockViewInverseY => BuiltIn.Position, - _ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.") - }; + var dict = isPerPatch + ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch) + : (isOutput ? context.Outputs : context.Inputs); + dict.Add(ioDefinition, spvVar); } private static string GetStagePrefix(ShaderStage stage) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs index aa3d046a..72541774 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs @@ -1,5 +1,4 @@ -using Ryujinx.Graphics.Shader.Translation; -using System; +using System; using static Spv.Specification; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index b3db1905..b6ffdb7a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.ImageLoad, GenerateImageLoad); Add(Instruction.ImageStore, GenerateImageStore); Add(Instruction.IsNan, GenerateIsNan); - Add(Instruction.LoadAttribute, GenerateLoadAttribute); + Add(Instruction.Load, GenerateLoad); Add(Instruction.LoadConstant, GenerateLoadConstant); Add(Instruction.LoadLocal, GenerateLoadLocal); Add(Instruction.LoadShared, GenerateLoadShared); @@ -133,7 +133,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.Store, GenerateStore); Add(Instruction.StoreLocal, GenerateStoreLocal); Add(Instruction.StoreShared, GenerateStoreShared); Add(Instruction.StoreShared16, GenerateStoreShared16); @@ -862,31 +862,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.Bool, result); } - private static OperationResult GenerateLoadAttribute(CodeGenContext context, AstOperation operation) + private static OperationResult GenerateLoad(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.LoadAttribute)} must be a constant operand."); - } - - var index = context.Get(AggregateType.S32, src3); - var resultType = AggregateType.FP32; - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2); - bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0; - return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index)); - } - else - { - var attr = context.Get(AggregateType.S32, src2); - return new OperationResult(resultType, context.GetAttribute(resultType, attr, isOutAttr: false, index)); - } + return GenerateLoadOrStore(context, operation, isStore: false); } private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation) @@ -1224,7 +1202,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask); var indexNotSegMask = context.BitwiseAnd(context.TypeU32(), index, notSegMask); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1254,7 +1232,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var notSegMask = context.Not(context.TypeU32(), segMask); var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1281,7 +1259,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var srcThreadId = context.ISub(context.TypeU32(), threadId, index); @@ -1310,7 +1288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var notSegMask = context.Not(context.TypeU32(), segMask); var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1336,35 +1314,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateUnary(context, operation, context.Delegates.GlslSqrt, null); } - private static OperationResult GenerateStoreAttribute(CodeGenContext context, AstOperation operation) + private static OperationResult GenerateStore(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 & AttributeConsts.Mask) + (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; + return GenerateLoadOrStore(context, operation, isStore: true); } private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation) @@ -1448,7 +1400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var three = context.Constant(context.TypeU32(), 3); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var shift = context.BitwiseAnd(context.TypeU32(), threadId, three); shift = context.ShiftLeftLogical(context.TypeU32(), shift, context.Constant(context.TypeU32(), 1)); var lutIdx = context.ShiftRightLogical(context.TypeU32(), mask, shift); @@ -1982,20 +1934,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var value = context.GetU32(operation.GetSource(2)); SpvInstruction elemPointer; - Instruction mr = operation.Inst & Instruction.MrMask; - if (mr == Instruction.MrStorage) + if (operation.StorageKind == StorageKind.StorageBuffer) { elemPointer = GetStorageElemPointer(context, operation); } - else if (mr == Instruction.MrShared) + else if (operation.StorageKind == StorageKind.SharedMemory) { var offset = context.GetU32(operation.GetSource(0)); elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset); } else { - throw new InvalidOperationException($"Invalid storage class \"{mr}\"."); + throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); } var one = context.Constant(context.TypeU32(), 1); @@ -2010,20 +1961,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var value1 = context.GetU32(operation.GetSource(3)); SpvInstruction elemPointer; - Instruction mr = operation.Inst & Instruction.MrMask; - if (mr == Instruction.MrStorage) + if (operation.StorageKind == StorageKind.StorageBuffer) { elemPointer = GetStorageElemPointer(context, operation); } - else if (mr == Instruction.MrShared) + else if (operation.StorageKind == StorageKind.SharedMemory) { var offset = context.GetU32(operation.GetSource(0)); elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset); } else { - throw new InvalidOperationException($"Invalid storage class \"{mr}\"."); + throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); } var one = context.Constant(context.TypeU32(), 1); @@ -2032,6 +1982,163 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.U32, context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, value1, value0)); } + private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + { + StorageKind storageKind = operation.StorageKind; + + SpvInstruction pointer; + AggregateType varType; + int srcIndex = 0; + + switch (storageKind) + { + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (!(operation.GetSource(srcIndex++) is AstOperand varId) || varId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + IoVariable ioVariable = (IoVariable)varId.Value; + bool isOutput = storageKind.IsOutput(); + bool isPerPatch = storageKind.IsPerPatch(); + int location = 0; + int component = 0; + + if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (!(operation.GetSource(srcIndex++) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + location = vecIndex.Value; + + if (operation.SourcesCount > srcIndex && + operation.GetSource(srcIndex) is AstOperand elemIndex && + elemIndex.Type == OperandType.Constant && + context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + srcIndex++; + } + } + + if (ioVariable == IoVariable.UserDefined) + { + varType = context.Config.GetUserDefinedType(location, isOutput); + } + else if (ioVariable == IoVariable.FragmentOutputColor) + { + varType = context.Config.GetFragmentOutputColorType(location); + } + else if (ioVariable == IoVariable.FragmentOutputIsBgra) + { + var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeU32()); + var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 1), elemIndex); + varType = AggregateType.U32; + + break; + } + else if (ioVariable == IoVariable.SupportBlockRenderScale) + { + var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); + var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 4), elemIndex); + varType = AggregateType.FP32; + + break; + } + else if (ioVariable == IoVariable.SupportBlockViewInverse) + { + var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); + var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 2), elemIndex); + varType = AggregateType.FP32; + + break; + } + else + { + (_, varType) = IoMap.GetSpirvBuiltIn(ioVariable); + } + + varType &= AggregateType.ElementTypeMask; + + int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex; + var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; + + var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component); + var dict = isPerPatch + ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch) + : (isOutput ? context.Outputs : context.Inputs); + + SpvInstruction baseObj = dict[ioDefinition]; + SpvInstruction e0, e1, e2; + + switch (inputsCount) + { + case 0: + pointer = baseObj; + break; + case 1: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0); + break; + case 2: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1); + break; + case 3: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2); + break; + default: + var indexes = new SpvInstruction[inputsCount]; + int index = 0; + + for (; index < inputsCount; srcIndex++, index++) + { + indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex)); + } + + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes); + break; + } + break; + + default: + throw new InvalidOperationException($"Invalid storage kind {storageKind}."); + } + + if (isStore) + { + context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex))); + return OperationResult.Invalid; + } + else + { + var result = context.Load(context.GetType(varType), pointer); + return new OperationResult(varType, result); + } + } + + private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable) + { + (_, var varType) = IoMap.GetSpirvBuiltIn(ioVariable); + varType &= AggregateType.ElementTypeMask; + + var ioDefinition = new IoDefinition(StorageKind.Input, ioVariable); + + return context.Load(context.GetType(varType), context.Inputs[ioDefinition]); + } + private static void GenerateStoreSharedSmallInt(CodeGenContext context, AstOperation operation, int bitSize) { var offset = context.Get(AggregateType.U32, operation.GetSource(0)); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs new file mode 100644 index 00000000..d2ff0085 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs @@ -0,0 +1,86 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using static Spv.Specification; + +namespace Ryujinx.Graphics.Shader.CodeGen.Spirv +{ + static class IoMap + { + // At least 16 attributes are guaranteed by the spec. + private const int MaxAttributes = 16; + + public static (BuiltIn, AggregateType) GetSpirvBuiltIn(IoVariable ioVariable) + { + return ioVariable switch + { + IoVariable.BaseInstance => (BuiltIn.BaseInstance, AggregateType.S32), + IoVariable.BaseVertex => (BuiltIn.BaseVertex, AggregateType.S32), + IoVariable.ClipDistance => (BuiltIn.ClipDistance, AggregateType.Array | AggregateType.FP32), + IoVariable.CtaId => (BuiltIn.WorkgroupId, AggregateType.Vector3 | AggregateType.U32), + IoVariable.DrawIndex => (BuiltIn.DrawIndex, AggregateType.S32), + IoVariable.FragmentCoord => (BuiltIn.FragCoord, AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentOutputDepth => (BuiltIn.FragDepth, AggregateType.FP32), + IoVariable.FrontFacing => (BuiltIn.FrontFacing, AggregateType.Bool), + IoVariable.InstanceId => (BuiltIn.InstanceId, AggregateType.S32), + IoVariable.InstanceIndex => (BuiltIn.InstanceIndex, AggregateType.S32), + IoVariable.InvocationId => (BuiltIn.InvocationId, AggregateType.S32), + IoVariable.Layer => (BuiltIn.Layer, AggregateType.S32), + IoVariable.PatchVertices => (BuiltIn.PatchVertices, AggregateType.S32), + IoVariable.PointCoord => (BuiltIn.PointCoord, AggregateType.Vector2 | AggregateType.FP32), + IoVariable.PointSize => (BuiltIn.PointSize, AggregateType.FP32), + IoVariable.Position => (BuiltIn.Position, AggregateType.Vector4 | AggregateType.FP32), + IoVariable.PrimitiveId => (BuiltIn.PrimitiveId, AggregateType.S32), + IoVariable.SubgroupEqMask => (BuiltIn.SubgroupEqMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupGeMask => (BuiltIn.SubgroupGeMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupGtMask => (BuiltIn.SubgroupGtMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupLaneId => (BuiltIn.SubgroupLocalInvocationId, AggregateType.U32), + IoVariable.SubgroupLeMask => (BuiltIn.SubgroupLeMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupLtMask => (BuiltIn.SubgroupLtMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.TessellationCoord => (BuiltIn.TessCoord, AggregateType.Vector3 | AggregateType.FP32), + IoVariable.TessellationLevelInner => (BuiltIn.TessLevelInner, AggregateType.Array | AggregateType.FP32), + IoVariable.TessellationLevelOuter => (BuiltIn.TessLevelOuter, AggregateType.Array | AggregateType.FP32), + IoVariable.ThreadId => (BuiltIn.LocalInvocationId, AggregateType.Vector3 | AggregateType.U32), + IoVariable.ThreadKill => (BuiltIn.HelperInvocation, AggregateType.Bool), + IoVariable.VertexId => (BuiltIn.VertexId, AggregateType.S32), + IoVariable.VertexIndex => (BuiltIn.VertexIndex, AggregateType.S32), + IoVariable.ViewportIndex => (BuiltIn.ViewportIndex, AggregateType.S32), + IoVariable.ViewportMask => (BuiltIn.ViewportMaskNV, AggregateType.Array | AggregateType.S32), + _ => (default, AggregateType.Invalid) + }; + } + + public static int GetSpirvBuiltInArrayLength(IoVariable ioVariable) + { + return ioVariable switch + { + IoVariable.ClipDistance => 8, + IoVariable.TessellationLevelInner => 2, + IoVariable.TessellationLevelOuter => 4, + IoVariable.ViewportMask => 1, + IoVariable.UserDefined => MaxAttributes, + _ => 1 + }; + } + + public static bool IsPerVertex(IoVariable ioVariable, ShaderStage stage, bool isOutput) + { + switch (ioVariable) + { + case IoVariable.Layer: + case IoVariable.ViewportIndex: + case IoVariable.PointSize: + case IoVariable.Position: + case IoVariable.UserDefined: + case IoVariable.ClipDistance: + case IoVariable.PointCoord: + case IoVariable.ViewportMask: + return !isOutput && + (stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry); + } + + return false; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs index 8503771c..f6c218c6 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var vectorFloat = context.ConvertSToF(vector2Type, vector); var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated); - var fragCoordPointer = context.Inputs[AttributeConsts.PositionX]; + var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)]; var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer); var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index ca823538..3e11a974 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (config.Stage == ShaderStage.Fragment) { - if (context.Info.Inputs.Contains(AttributeConsts.Layer)) + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer))) { context.AddCapability(Capability.Geometry); } @@ -93,13 +93,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.DrawParameters); } - Declarations.DeclareAll(context, info); + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.ViewportMask))) + { + context.AddExtension("SPV_NV_viewport_array2"); + context.AddCapability(Capability.ShaderViewportMaskNV); + } if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0) { - Declarations.DeclareInvocationId(context); + info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.SubgroupLaneId)); } + Declarations.DeclareAll(context, info); + for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++) { var function = info.Functions[funcIndex]; @@ -203,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (context.Config.Options.TargetApi == TargetApi.Vulkan) { - // We invert the front face on Vulkan backend, so we need to do that here aswell. + // We invert the front face on Vulkan backend, so we need to do that here as well. tessCw = !tessCw; } @@ -250,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); - if (context.Outputs.ContainsKey(AttributeConsts.FragmentOutputDepth)) + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.FragmentOutputDepth))) { context.AddExecutionMode(spvFunc, ExecutionMode.DepthReplacing); } @@ -389,21 +395,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var source = context.Get(dest.VarType, assignment.Source); context.Store(context.GetLocalPointer(dest), source); } - else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) - { - bool perPatch = dest.Type == OperandType.AttributePerPatch; - - if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch)) - { - 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)); - } - } else if (dest.Type == OperandType.Argument) { var source = context.Get(dest.VarType, assignment.Source); |
