From e1d73cc5605f7793eca5d2576df42244640596d2 Mon Sep 17 00:00:00 2001 From: gdk Date: Mon, 14 Feb 2022 15:53:35 -0300 Subject: [PATCH] SPIR-V: Geometry shader support --- Ryujinx.Graphics.Gpu/Constants.cs | 2 +- .../Engine/Threed/ThreedClassState.cs | 4 +- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- .../CodeGen/Glsl/OperandManager.cs | 14 +----- .../CodeGen/Spirv/CodeGenContext.cs | 47 ++++++++++++++----- .../CodeGen/Spirv/Declarations.cs | 15 +++--- .../CodeGen/Spirv/Instructions.cs | 42 ++++++++++++++--- .../CodeGen/Spirv/SpirvGenerator.cs | 42 ++++++++++++++++- .../StructuredIr/StructuredProgram.cs | 12 +++++ .../StructuredIr/StructuredProgramContext.cs | 2 +- .../Translation/AttributeInfo.cs | 15 +++++- .../Translation/EmitterContext.cs | 4 +- 12 files changed, 150 insertions(+), 51 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/Ryujinx.Graphics.Gpu/Constants.cs index 455029f54..1738fddf7 100644 --- a/Ryujinx.Graphics.Gpu/Constants.cs +++ b/Ryujinx.Graphics.Gpu/Constants.cs @@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu /// /// Maximum number of vertex attributes. /// - public const int TotalVertexAttribs = 16; + public const int TotalVertexAttribs = 16; // FIXME: Should be 32, but OpenGL only supports 16. /// /// Maximum number of vertex buffers. diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index 172e8c370..26260ce59 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -768,8 +768,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public fixed uint Reserved10B0[18]; public uint ClearFlags; public fixed uint Reserved10FC[25]; - public Array16 VertexAttribState; - public fixed uint Reserved11A0[31]; + public Array32 VertexAttribState; + public fixed uint Reserved11E0[15]; public RtControl RtControl; public fixed uint Reserved1220[2]; public Size3D RtDepthStencilSize; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 9ffece6a2..8cacd8956 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Gpu.Shader return gpShaders; } - AttributeType[] attributeTypes = new AttributeType[Constants.TotalVertexAttribs]; + AttributeType[] attributeTypes = new AttributeType[32]; for (int location = 0; location < attributeTypes.Length; location++) { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index c5d60ee16..74dfd0236 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -275,7 +275,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string name = builtInAttr.Name; - if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr) && IsArrayBuiltIn(value)) + if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) { name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; } @@ -317,18 +317,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static bool IsArrayBuiltIn(int attr) - { - if (attr <= AttributeConsts.TessLevelInner1 || - attr == AttributeConsts.TessCoordX || - attr == AttributeConsts.TessCoordY) - { - return false; - } - - return (attr & AttributeConsts.SpecialMask) == 0; - } - public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable) { if (cbIndexable) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 84de8bd6e..28d0aeab7 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { public ShaderConfig Config { get; } + public int InputVertices { get; } + public Dictionary UniformBuffers { get; } = new Dictionary(); public Instruction StorageBuffersArray { get; set; } public Instruction LocalMemory { get; set; } @@ -65,6 +67,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { Config = config; + if (config.Stage == ShaderStage.Geometry) + { + InputTopology inPrimitive = config.GpuAccessor.QueryPrimitiveTopology(); + + InputVertices = inPrimitive switch + { + InputTopology.Points => 1, + InputTopology.Lines => 2, + InputTopology.LinesAdjacency => 2, + InputTopology.Triangles => 3, + InputTopology.TrianglesAdjacency => 3, + _ => throw new InvalidOperationException($"Invalid input topology \"{inPrimitive}\".") + }; + } + AddCapability(Capability.Shader); AddCapability(Capability.Float64); @@ -171,7 +188,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return operand.Type switch { IrOperandType.Argument => GetArgument(type, operand), - IrOperandType.Attribute => GetAttribute(type, operand, false), + IrOperandType.Attribute => GetAttribute(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), @@ -190,16 +207,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return isOutAttr ? Outputs[attrInfo.BaseValue] : Inputs[attrInfo.BaseValue]; } - public Instruction GetAttributeElemPointer(AstOperand operand, bool isOutAttr, out AggregateType elemType) + public Instruction GetAttributeElemPointer(int attr, bool isOutAttr, Instruction index, out AggregateType elemType) { - var attrInfo = AttributeInfo.From(Config, operand.Value); - if (attrInfo.BaseValue == AttributeConsts.PositionX && Config.Stage != ShaderStage.Fragment) - { - isOutAttr = true; - } + var attrInfo = AttributeInfo.From(Config, attr); elemType = attrInfo.Type & AggregateType.ElementTypeMask; + var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; + + var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); + var ioVariable = isOutAttr ? Outputs[attrInfo.BaseValue] : Inputs[attrInfo.BaseValue]; if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0) @@ -207,15 +224,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return ioVariable; } - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - - var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex); + if (Config.Stage == ShaderStage.Geometry && !isOutAttr && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex); + } + else + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex); + } } - public Instruction GetAttribute(AggregateType type, AstOperand operand, bool isOutAttr) + public Instruction GetAttribute(AggregateType type, int attr, bool isOutAttr, Instruction index = null) { - var elemPointer = GetAttributeElemPointer(operand, isOutAttr, out var elemType); + var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType); return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer)); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index b097f64b9..83f94dbf6 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -391,19 +391,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var dict = isOutAttr ? context.Outputs : context.Inputs; var attrInfo = AttributeInfo.From(context.Config, attr); - if (attrInfo.BaseValue == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment) - { - isOutAttr = true; - dict = context.Outputs; - } - if (dict.ContainsKey(attrInfo.BaseValue)) { return; } var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var spvType = context.TypePointer(storageClass, context.GetType(attrInfo.Type, attrInfo.Length)); + var attrType = context.GetType(attrInfo.Type, attrInfo.Length); + + if (context.Config.Stage == ShaderStage.Geometry && !isOutAttr && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) + { + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices)); + } + + var spvType = context.TypePointer(storageClass, attrType); var spvVar = context.Variable(spvType, storageClass); if (attrInfo.IsBuiltin) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 3375e8d64..f1d72a95b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -73,6 +73,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.Ddy, GenerateDdy); Add(Instruction.Discard, GenerateDiscard); Add(Instruction.Divide, GenerateDivide); + Add(Instruction.EmitVertex, GenerateEmitVertex); + Add(Instruction.EndPrimitive, GenerateEndPrimitive); Add(Instruction.ExponentB2, GenerateExponentB2); Add(Instruction.FindLSB, GenerateFindLSB); Add(Instruction.FindMSBS32, GenerateFindMSBS32); @@ -500,6 +502,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateBinary(context, operation, context.FDiv, context.SDiv); } + private static OperationResult GenerateEmitVertex(CodeGenContext context, AstOperation operation) + { + context.EmitVertex(); + + return OperationResult.Invalid; + } + + private static OperationResult GenerateEndPrimitive(CodeGenContext context, AstOperation operation) + { + context.EndPrimitive(); + + return OperationResult.Invalid; + } + private static OperationResult GenerateExponentB2(CodeGenContext context, AstOperation operation) { return GenerateUnary(context, operation, context.GlslExp2, null); @@ -812,13 +828,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var src1 = operation.GetSource(0); var src2 = operation.GetSource(1); + var src3 = operation.GetSource(2); - if (src1 is not AstOperand oper || oper.Type != OperandType.Attribute) + if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant) { - throw new InvalidOperationException("First source of LoadAttribute must be a attribute."); + throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand."); } - return new OperationResult(AggregateType.FP32, context.GetAttribute(AggregateType.FP32, oper, false)); + var index = context.Get(AggregateType.S32, src3); + var resultType = AggregateType.FP32; + + if (src2 is AstOperand operand && operand.Type == OperandType.Constant) + { + int attrOffset = baseAttr.Value + (operand.Value << 2); + return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, false, index)); + } + else + { + throw new NotImplementedException(); + } } private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation) @@ -1081,7 +1109,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, new AstOperand(OperandType.Attribute, AttributeConsts.LaneId), false); + var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1111,7 +1139,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, new AstOperand(OperandType.Attribute, AttributeConsts.LaneId), false); + var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1138,7 +1166,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, new AstOperand(OperandType.Attribute, AttributeConsts.LaneId), false); + var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var srcThreadId = context.ISub(context.TypeU32(), threadId, index); @@ -1167,7 +1195,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, new AstOperand(OperandType.Attribute, AttributeConsts.LaneId), false); + var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 67caa18e9..269635a0b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -29,6 +29,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.SampledBuffer); context.AddCapability(Capability.SubgroupBallotKHR); context.AddCapability(Capability.SubgroupVoteKHR); + + if (config.Stage == ShaderStage.Geometry) + { + context.AddCapability(Capability.Geometry); + } + context.AddExtension("SPV_KHR_shader_ballot"); context.AddExtension("SPV_KHR_subgroup_vote"); @@ -97,7 +103,39 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface()); - if (context.Config.Stage == ShaderStage.Fragment) + if (context.Config.Stage == ShaderStage.Geometry) + { + InputTopology inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology(); + + switch (inPrimitive) + { + case InputTopology.Points: + context.AddExecutionMode(spvFunc, ExecutionMode.InputPoints); + break; + case InputTopology.Lines: + context.AddExecutionMode(spvFunc, ExecutionMode.InputLines); + break; + case InputTopology.LinesAdjacency: + context.AddExecutionMode(spvFunc, ExecutionMode.InputLinesAdjacency); + break; + case InputTopology.TrianglesAdjacency: + context.AddExecutionMode(spvFunc, ExecutionMode.InputTrianglesAdjacency); + break; + } + + context.AddExecutionMode(spvFunc, ExecutionMode.Invocations, (SpvLiteralInteger)context.InputVertices); + + context.AddExecutionMode(spvFunc, context.Config.OutputTopology switch + { + OutputTopology.PointList => ExecutionMode.OutputPoints, + OutputTopology.LineStrip => ExecutionMode.OutputLineStrip, + OutputTopology.TriangleStrip => ExecutionMode.OutputTriangleStrip, + _ => throw new InvalidOperationException($"Invalid output topology \"{context.Config.OutputTopology}\".") + }); + + context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.MaxOutputVertices); + } + else if (context.Config.Stage == ShaderStage.Fragment) { context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan ? ExecutionMode.OriginUpperLeft @@ -228,7 +266,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (dest.Type == OperandType.Attribute) { - var elemPointer = context.GetAttributeElemPointer(dest, true, out var elemType); + var elemPointer = context.GetAttributeElemPointer(dest.Value, true, null, out var elemType); context.Store(elemPointer, context.Get(elemType, assignment.Source)); } else if (dest.Type == OperandType.Argument) diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index ce79f3b8e..0ec56441c 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -89,6 +89,18 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { Instruction inst = operation.Inst; + if (inst == Instruction.LoadAttribute) + { + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant) + { + int attrOffset = src1.Value + (src2.Value << 2); + context.Info.Inputs.Add(attrOffset); + } + } + int sourcesCount = operation.SourcesCount; int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0; diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 4e3f3e28a..2c87721ee 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { if (operand.Type == OperandType.Attribute) { - Info.Outputs.Add(operand.Value); + Info.Outputs.Add(operand.Value & AttributeConsts.Mask); } return GetOperand(operand); diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index c39e655e4..d0394969d 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -1,5 +1,4 @@ -using Ryujinx.Common; -using System.Collections.Generic; +using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation { @@ -98,5 +97,17 @@ namespace Ryujinx.Graphics.Shader.Translation return new AttributeInfo(value, 0, 0, AggregateType.Invalid); } + + public static bool IsArrayBuiltIn(int attr) + { + if (attr <= AttributeConsts.TessLevelInner1 || + attr == AttributeConsts.TessCoordX || + attr == AttributeConsts.TessCoordY) + { + return false; + } + + return (attr & AttributeConsts.SpecialMask) == 0; + } } } diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 237a66aa2..dbf4eb125 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -183,8 +183,8 @@ namespace Ryujinx.Graphics.Shader.Translation if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) { - Operand z = Attribute(AttributeConsts.PositionZ); - Operand w = Attribute(AttributeConsts.PositionW); + Operand z = Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask); + Operand w = Attribute(AttributeConsts.PositionW | AttributeConsts.LoadOutputMask); Operand halfW = this.FPMultiply(w, ConstF(0.5f)); this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));