aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Shader/CodeGen')
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs35
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs16
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs20
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs4
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs158
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs145
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs345
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs255
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs380
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs3
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs241
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs86
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs2
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs31
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);