mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-21 00:23:36 +00:00
SPIR-V: Geometry shader support
This commit is contained in:
parent
bd56998f4d
commit
e1d73cc560
12 changed files with 150 additions and 51 deletions
|
@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// <summary>
|
||||
/// Maximum number of vertex attributes.
|
||||
/// </summary>
|
||||
public const int TotalVertexAttribs = 16;
|
||||
public const int TotalVertexAttribs = 16; // FIXME: Should be 32, but OpenGL only supports 16.
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of vertex buffers.
|
||||
|
|
|
@ -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> VertexAttribState;
|
||||
public fixed uint Reserved11A0[31];
|
||||
public Array32<VertexAttribState> VertexAttribState;
|
||||
public fixed uint Reserved11E0[15];
|
||||
public RtControl RtControl;
|
||||
public fixed uint Reserved1220[2];
|
||||
public Size3D RtDepthStencilSize;
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
public ShaderConfig Config { get; }
|
||||
|
||||
public int InputVertices { get; }
|
||||
|
||||
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue