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