diff options
Diffstat (limited to 'Ryujinx.Graphics.Shader/CodeGen')
14 files changed, 712 insertions, 1009 deletions
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 5e53d62a..81b79ec4 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -59,6 +59,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable"); } + if (context.Config.GpuAccessor.QueryHostSupportsViewportMask()) + { + context.AppendLine("#extension GL_NV_viewport_array2 : enable"); + } + context.AppendLine("#pragma optionNV(fastmath off)"); context.AppendLine(); @@ -215,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { - var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX); + var tfOutput = context.Config.GetTransformFeedbackOutput(AttributeConsts.PositionX); if (tfOutput.Valid) { context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); @@ -552,7 +557,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr) { - string suffix = AttributeInfo.IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty; + string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty; string iq = string.Empty; if (context.Config.Stage == ShaderStage.Fragment) @@ -569,8 +574,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment) { - int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; - int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); + int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0); if (components > 1) { @@ -652,13 +656,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareOutputAttribute(CodeGenContext context, int attr) { - string suffix = AttributeInfo.IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty; + string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty; string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}"; if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { - int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; - int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); + int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0); if (components > 1) { @@ -672,7 +675,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string xfb = string.Empty; - var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset); + var tfOutput = context.Config.GetTransformFeedbackOutput(attr, 0); if (tfOutput.Valid) { xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; @@ -687,7 +690,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string xfb = string.Empty; - var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4); + var tfOutput = context.Config.GetTransformFeedbackOutput(attr, c); if (tfOutput.Valid) { xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; @@ -726,6 +729,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};"); } + private static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr) + { + if (isOutAttr) + { + return stage == ShaderStage.TessellationControl; + } + else + { + return stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry; + } + } + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) { foreach (int attr in attrs.Order()) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 90727558..751d0350 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -1,5 +1,4 @@ using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; @@ -126,21 +125,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (node is AstAssignment assignment) { + AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination); AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source); - AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true); - - string dest; - - if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute()) - { - bool perPatch = operand.Type == OperandType.AttributePerPatch; - dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch); - } - else - { - dest = InstGen.GetExpression(context, assignment.Destination); - } + string dest = InstGen.GetExpression(context, assignment.Destination); string src = ReinterpretCast(context, assignment.Source, srcType, dstType); context.AppendLine(dest + " = " + src + ";"); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 9ca4618d..01bd11e5 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // For shared memory access, the second argument is unused and should be ignored. // It is there to make both storage and shared access have the same number of arguments. // For storage, both inputs are consumed when the argument index is 0, so we should skip it here. - if (argIndex == 1 && (atomic || (inst & Instruction.MrMask) == Instruction.MrShared)) + if (argIndex == 1 && (atomic || operation.StorageKind == StorageKind.SharedMemory)) { continue; } @@ -85,14 +85,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (argIndex == 0 && atomic) { - Instruction memRegion = inst & Instruction.MrMask; - - switch (memRegion) + switch (operation.StorageKind) { - case Instruction.MrShared: args += LoadShared(context, operation); break; - case Instruction.MrStorage: args += LoadStorage(context, operation); break; + case StorageKind.SharedMemory: args += LoadShared(context, operation); break; + case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break; - default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\"."); + default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); } } else @@ -166,8 +164,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.ImageAtomic: return ImageLoadOrStore(context, operation); - case Instruction.LoadAttribute: - return LoadAttribute(context, operation); + case Instruction.Load: + return Load(context, operation); case Instruction.LoadConstant: return LoadConstant(context, operation); @@ -193,8 +191,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.PackHalf2x16: return PackHalf2x16(context, operation); - case Instruction.StoreAttribute: - return StoreAttribute(context, operation); + case Instruction.Store: + return Store(context, operation); case Instruction.StoreLocal: return StoreLocal(context, operation); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 743b695c..00478f6a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ImageStore, InstType.Special); Add(Instruction.ImageAtomic, InstType.Special); Add(Instruction.IsNan, InstType.CallUnary, "isnan"); - Add(Instruction.LoadAttribute, InstType.Special); + Add(Instruction.Load, InstType.Special); Add(Instruction.LoadConstant, InstType.Special); Add(Instruction.LoadLocal, InstType.Special); Add(Instruction.LoadShared, InstType.Special); @@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor); Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); - Add(Instruction.StoreAttribute, InstType.Special); + Add(Instruction.Store, InstType.Special); Add(Instruction.StoreLocal, InstType.Special); Add(Instruction.StoreShared, InstType.Special); Add(Instruction.StoreShared16, InstType.Special); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index a5d2632c..99519837 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -210,30 +210,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return texCallBuilder.ToString(); } - public static string LoadAttribute(CodeGenContext context, AstOperation operation) + public static string Load(CodeGenContext context, AstOperation operation) { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode 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."); - } - - string indexExpr = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2)); - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = baseAttr.Value + (operand.Value << 2); - return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr); - } - else - { - string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true); - return OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: false, indexExpr); - } + return GenerateLoadOrStore(context, operation, isStore: false); } public static string LoadConstant(CodeGenContext context, AstOperation operation) @@ -337,33 +316,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}"; } - public static string StoreAttribute(CodeGenContext context, AstOperation operation) + public static string Store(CodeGenContext context, AstOperation operation) { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode 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."); - } - - string attrName; - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = baseAttr.Value + (operand.Value << 2); - attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true); - } - else - { - string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true); - attrName = OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: true); - } - - string value = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2)); - return $"{attrName} = {value}"; + return GenerateLoadOrStore(context, operation, isStore: true); } public static string StoreLocal(CodeGenContext context, AstOperation operation) @@ -847,6 +802,111 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } + private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + { + StorageKind storageKind = operation.StorageKind; + + string varName; + 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 = -1; + 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++; + } + } + + (varName, varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch); + + if (IoMap.IsPerVertexBuiltIn(context.Config.Stage, ioVariable, isOutput)) + { + // Since those exist both as input and output on geometry and tessellation shaders, + // we need the gl_in and gl_out prefixes to disambiguate. + + if (storageKind == StorageKind.Input) + { + string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32); + varName = $"gl_in[{expr}].{varName}"; + } + else if (storageKind == StorageKind.Output) + { + string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32); + varName = $"gl_out[{expr}].{varName}"; + } + } + + int firstSrcIndex = srcIndex; + int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount; + + for (; srcIndex < inputsCount; srcIndex++) + { + IAstNode src = operation.GetSource(srcIndex); + + if ((varType & AggregateType.ElementCountMask) != 0 && + srcIndex == inputsCount - 1 && + src is AstOperand elementIndex && + elementIndex.Type == OperandType.Constant) + { + varName += "." + "xyzw"[elementIndex.Value & 3]; + } + else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output) + { + // GLSL requires that for tessellation control shader outputs, + // that the index expression must be *exactly* "gl_InvocationID", + // otherwise the compilation fails. + // TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR. + varName += "[gl_InvocationID]"; + } + else + { + varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]"; + } + } + break; + + default: + throw new InvalidOperationException($"Invalid storage kind {storageKind}."); + } + + if (isStore) + { + varType &= AggregateType.ElementTypeMask; + varName = $"{varName} = {GetSoureExpr(context, operation.GetSource(srcIndex), varType)}"; + } + + return varName; + } + private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage) { string sbName = OperandManager.GetShaderStagePrefix(stage); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs new file mode 100644 index 00000000..093ee232 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs @@ -0,0 +1,145 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System.Globalization; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class IoMap + { + public static (string, AggregateType) GetGlslVariable( + ShaderConfig config, + IoVariable ioVariable, + int location, + int component, + bool isOutput, + bool isPerPatch) + { + return ioVariable switch + { + IoVariable.BackColorDiffuse => ("gl_BackColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.BackColorSpecular => ("gl_BackSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.BaseInstance => ("gl_BaseInstanceARB", AggregateType.S32), + IoVariable.BaseVertex => ("gl_BaseVertexARB", AggregateType.S32), + IoVariable.ClipDistance => ("gl_ClipDistance", AggregateType.Array | AggregateType.FP32), + IoVariable.CtaId => ("gl_WorkGroupID", AggregateType.Vector3 | AggregateType.U32), + IoVariable.DrawIndex => ("gl_DrawIDARB", AggregateType.S32), + IoVariable.FogCoord => ("gl_FogFragCoord", AggregateType.FP32), // Deprecated. + IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location), + IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32), + IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool), + IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool), + IoVariable.InstanceId => ("gl_InstanceID", AggregateType.S32), + IoVariable.InstanceIndex => ("gl_InstanceIndex", AggregateType.S32), + IoVariable.InvocationId => ("gl_InvocationID", AggregateType.S32), + IoVariable.Layer => ("gl_Layer", AggregateType.S32), + IoVariable.PatchVertices => ("gl_PatchVerticesIn", AggregateType.S32), + IoVariable.PointCoord => ("gl_PointCoord", AggregateType.Vector2 | AggregateType.FP32), + IoVariable.PointSize => ("gl_PointSize", AggregateType.FP32), + IoVariable.Position => ("gl_Position", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.PrimitiveId => GetPrimitiveIdVariableName(config.Stage, isOutput), + IoVariable.SubgroupEqMask => GetSubgroupMaskVariableName(config, "Eq"), + IoVariable.SubgroupGeMask => GetSubgroupMaskVariableName(config, "Ge"), + IoVariable.SubgroupGtMask => GetSubgroupMaskVariableName(config, "Gt"), + IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config), + IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"), + IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"), + IoVariable.SupportBlockRenderScale => (DefaultNames.SupportBlockRenderScaleName, AggregateType.Array | AggregateType.FP32), + IoVariable.SupportBlockViewInverse => (DefaultNames.SupportBlockViewportInverse, AggregateType.Vector2 | AggregateType.FP32), + IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32), + IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32), + IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32), + IoVariable.TextureCoord => ("gl_TexCoord", AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.ThreadId => ("gl_LocalInvocationID", AggregateType.Vector3 | AggregateType.U32), + IoVariable.ThreadKill => ("gl_HelperInvocation", AggregateType.Bool), + IoVariable.UserDefined => GetUserDefinedVariableName(config, location, component, isOutput, isPerPatch), + IoVariable.VertexId => ("gl_VertexID", AggregateType.S32), + IoVariable.VertexIndex => ("gl_VertexIndex", AggregateType.S32), + IoVariable.ViewportIndex => ("gl_ViewportIndex", AggregateType.S32), + IoVariable.ViewportMask => ("gl_ViewportMask", AggregateType.Array | AggregateType.S32), + _ => (null, AggregateType.Invalid) + }; + } + + public static bool IsPerVertexBuiltIn(ShaderStage stage, IoVariable ioVariable, bool isOutput) + { + switch (ioVariable) + { + case IoVariable.Layer: + case IoVariable.ViewportIndex: + case IoVariable.PointSize: + case IoVariable.Position: + case IoVariable.ClipDistance: + case IoVariable.PointCoord: + case IoVariable.ViewportMask: + if (isOutput) + { + return stage == ShaderStage.TessellationControl; + } + else + { + return stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry; + } + } + + return false; + } + + private static (string, AggregateType) GetFragmentOutputColorVariableName(ShaderConfig config, int location) + { + if (location < 0) + { + return (DefaultNames.OAttributePrefix, config.GetFragmentOutputColorType(0)); + } + + string name = DefaultNames.OAttributePrefix + location.ToString(CultureInfo.InvariantCulture); + + return (name, config.GetFragmentOutputColorType(location)); + } + + private static (string, AggregateType) GetPrimitiveIdVariableName(ShaderStage stage, bool isOutput) + { + // The geometry stage has an additional gl_PrimitiveIDIn variable. + return (isOutput || stage != ShaderStage.Geometry ? "gl_PrimitiveID" : "gl_PrimitiveIDIn", AggregateType.S32); + } + + private static (string, AggregateType) GetSubgroupMaskVariableName(ShaderConfig config, string cc) + { + return config.GpuAccessor.QueryHostSupportsShaderBallot() + ? ($"unpackUint2x32(gl_SubGroup{cc}MaskARB)", AggregateType.Vector2 | AggregateType.U32) + : ($"gl_Subgroup{cc}Mask", AggregateType.Vector4 | AggregateType.U32); + } + + private static (string, AggregateType) GetSubgroupInvocationIdVariableName(ShaderConfig config) + { + return config.GpuAccessor.QueryHostSupportsShaderBallot() + ? ("gl_SubGroupInvocationARB", AggregateType.U32) + : ("gl_SubgroupInvocationID", AggregateType.U32); + } + + private static (string, AggregateType) GetUserDefinedVariableName(ShaderConfig config, int location, int component, bool isOutput, bool isPerPatch) + { + string name = isPerPatch + ? DefaultNames.PerPatchAttributePrefix + : (isOutput ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix); + + if (location < 0) + { + return (name, config.GetUserDefinedType(0, isOutput)); + } + + name += location.ToString(CultureInfo.InvariantCulture); + + if (config.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) + { + name += "_" + "xyzw"[component & 3]; + } + + return (name, config.GetUserDefinedType(location, isOutput)); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index ec761fa6..92e83358 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -1,10 +1,10 @@ +using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Numerics; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -12,82 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { class OperandManager { - private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; - - private readonly struct BuiltInAttribute - { - public string Name { get; } - - public AggregateType Type { get; } - - public BuiltInAttribute(string name, AggregateType type) - { - Name = name; - Type = type; - } - } - - private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>() - { - { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) }, - { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) }, - { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) }, - { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) }, - { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) }, - { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) }, - { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) }, - { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) }, - { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) }, - { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) }, - { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) }, - { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) }, - { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) }, - { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) }, - { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) }, - { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) }, - { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) }, - { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) }, - { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) }, - - // Special. - { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) }, - { AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) }, - { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) }, - { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) }, - { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) }, - { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) }, - { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) }, - { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) }, - { AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) }, - { AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) }, - { AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) }, - { AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) }, - - // Support uniforms. - { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) }, - - { AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) }, - { AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) } - }; + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private Dictionary<AstOperand, string> _locals; @@ -110,8 +35,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false), - OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config), OperandType.LocalVariable => _locals[operand], @@ -155,177 +78,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } - public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch) - { - return GetAttributeName(context, value, perPatch, isOutAttr: true); - } - - public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0") - { - ShaderConfig config = context.Config; - - if ((value & AttributeConsts.LoadOutputMask) != 0) - { - isOutAttr = true; - } - - value &= AttributeConsts.Mask & ~3; - char swzMask = GetSwizzleMask((value >> 2) & 3); - - if (perPatch) - { - if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd) - { - value -= AttributeConsts.UserAttributePerPatchBase; - - return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}"; - } - else if (value < AttributeConsts.UserAttributePerPatchBase) - { - return value switch - { - AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]", - AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]", - AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]", - AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]", - AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]", - AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]", - _ => null - }; - } - } - else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) - { - int attrOffset = value; - value -= AttributeConsts.UserAttributeBase; - - string prefix = isOutAttr - ? DefaultNames.OAttributePrefix - : DefaultNames.IAttributePrefix; - - bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing); - - if (indexable) - { - string name = prefix; - - if (config.Stage == ShaderStage.Geometry && !isOutAttr) - { - name += $"[{indexExpr}]"; - } - - return name + $"[{(value >> 4)}]." + swzMask; - } - else if (config.TransformFeedbackEnabled && - ((config.LastInVertexPipeline && isOutAttr) || - (config.Stage == ShaderStage.Fragment && !isOutAttr))) - { - int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); - string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}"; - - if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) - { - name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; - } - - return components > 1 ? name + '.' + swzMask : name; - } - else - { - string name = $"{prefix}{(value >> 4)}"; - - if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) - { - name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; - } - - return name + '.' + swzMask; - } - } - else - { - if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) - { - value -= AttributeConsts.FragmentOutputColorBase; - - return $"{DefaultNames.OAttributePrefix}{(value >> 4)}.{swzMask}"; - } - else if (_builtInAttributes.TryGetValue(value, out BuiltInAttribute builtInAttr)) - { - string subgroupMask = value switch - { - AttributeConsts.EqMask => "Eq", - AttributeConsts.GeMask => "Ge", - AttributeConsts.GtMask => "Gt", - AttributeConsts.LeMask => "Le", - AttributeConsts.LtMask => "Lt", - _ => null - }; - - if (subgroupMask != null) - { - return config.GpuAccessor.QueryHostSupportsShaderBallot() - ? $"unpackUint2x32(gl_SubGroup{subgroupMask}MaskARB).x" - : $"gl_Subgroup{subgroupMask}Mask.x"; - } - else if (value == AttributeConsts.LaneId) - { - return config.GpuAccessor.QueryHostSupportsShaderBallot() - ? "gl_SubGroupInvocationARB" - : "gl_SubgroupInvocationID"; - } - - if (config.Stage == ShaderStage.Fragment) - { - // TODO: There must be a better way to handle this... - switch (value) - { - case AttributeConsts.PositionX: return $"(gl_FragCoord.x / {DefaultNames.SupportBlockRenderScaleName}[0])"; - case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])"; - case AttributeConsts.PositionZ: return "gl_FragCoord.z"; - case AttributeConsts.PositionW: return "gl_FragCoord.w"; - - case AttributeConsts.FrontFacing: - if (config.GpuAccessor.QueryHostHasFrontFacingBug()) - { - // This is required for Intel on Windows, gl_FrontFacing sometimes returns incorrect - // (flipped) values. Doing this seems to fix it. - return "(-floatBitsToInt(float(gl_FrontFacing)) < 0)"; - } - break; - } - } - - string name = builtInAttr.Name; - - if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) - { - name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; - } - - return name; - } - } - - // TODO: Warn about unknown built-in attribute. - - return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0"; - } - - public static string GetAttributeName(string attrExpr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0") - { - string name = isOutAttr - ? DefaultNames.OAttributePrefix - : DefaultNames.IAttributePrefix; - - if (config.Stage == ShaderStage.Geometry && !isOutAttr) - { - name += $"[{indexExpr}]"; - } - - return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]"; - } - public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable) { if (cbIndexable) @@ -387,12 +139,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { int index = (int)stage; - if ((uint)index >= StagePrefixes.Length) + if ((uint)index >= _stagePrefixes.Length) { return "invalid"; } - return StagePrefixes[index]; + return _stagePrefixes[index]; } private static char GetSwizzleMask(int value) @@ -405,24 +157,54 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; } - public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false) + public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) { + // TODO: Get rid of that function entirely and return the type from the operation generation + // functions directly, like SPIR-V does. + if (node is AstOperation operation) { - if (operation.Inst == Instruction.LoadAttribute) + if (operation.Inst == Instruction.Load) { - // Load attribute basically just returns the attribute value. - // Some built-in attributes may have different types, so we need - // to return the type based on the attribute that is being read. - if (operation.GetSource(0) is AstOperand operand && operand.Type == OperandType.Constant) + switch (operation.StorageKind) { - if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) - { - return builtInAttr.Type; - } - } + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (!(operation.GetSource(0) is AstOperand varId) || varId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + IoVariable ioVariable = (IoVariable)varId.Value; + bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; + bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; + int location = 0; + int component = 0; + + if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (!(operation.GetSource(1) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + location = vecIndex.Value; + + if (operation.SourcesCount > 2 && + operation.GetSource(2) is AstOperand elemIndex && + elemIndex.Type == OperandType.Constant && + context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + } + } - return OperandInfo.GetVarType(OperandType.Attribute); + (_, AggregateType varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch); + + return varType & AggregateType.ElementTypeMask; + } } else if (operation.Inst == Instruction.Call) { @@ -461,45 +243,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return context.CurrentFunction.GetArgumentType(argIndex); } - return GetOperandVarType(context, operand, isAsgDest); + return OperandInfo.GetVarType(operand); } else { throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); } } - - private static AggregateType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false) - { - if (operand.Type == OperandType.Attribute) - { - if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) - { - return builtInAttr.Type; - } - else if (context.Config.Stage == ShaderStage.Vertex && !isAsgDest && - operand.Value >= AttributeConsts.UserAttributeBase && - operand.Value < AttributeConsts.UserAttributeEnd) - { - int location = (operand.Value - AttributeConsts.UserAttributeBase) / 16; - - AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location); - - return type.ToAggregateType(); - } - else if (context.Config.Stage == ShaderStage.Fragment && isAsgDest && - operand.Value >= AttributeConsts.FragmentOutputColorBase && - operand.Value < AttributeConsts.FragmentOutputColorEnd) - { - int location = (operand.Value - AttributeConsts.FragmentOutputColorBase) / 16; - - AttributeType type = context.Config.GpuAccessor.QueryFragmentOutputType(location); - - return type.ToAggregateType(); - } - } - - return OperandInfo.GetVarType(operand); - } } }
\ No newline at end of file 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); |