aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Graphics.GAL/Capabilities.cs9
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs4
-rw-r--r--Ryujinx.Graphics.OpenGL/HwCapabilities.cs2
-rw-r--r--Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs3
-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
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Decoder.cs5
-rw-r--r--Ryujinx.Graphics.Shader/IGpuAccessor.cs19
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs351
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs150
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs24
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs36
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs11
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs51
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs10
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs10
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs18
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs39
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs10
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs2
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs6
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs44
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs2
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs77
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs67
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs49
-rw-r--r--Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs121
-rw-r--r--Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs210
-rw-r--r--Ryujinx.Graphics.Shader/Translation/EmitterContext.cs100
-rw-r--r--Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs109
-rw-r--r--Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs9
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs6
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs6
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Rewriter.cs23
-rw-r--r--Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs211
-rw-r--r--Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs72
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Translator.cs57
-rw-r--r--Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs47
-rw-r--r--Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs3
-rw-r--r--Ryujinx.Graphics.Vulkan/Shader.cs4
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanInitialization.cs3
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanRenderer.cs4
-rw-r--r--Ryujinx.ShaderTools/Program.cs4
56 files changed, 1966 insertions, 1745 deletions
diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index bc4a02c9..a93d3846 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -35,7 +35,8 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsNonConstantTextureOffset;
public readonly bool SupportsShaderBallot;
public readonly bool SupportsTextureShadowLod;
- public readonly bool SupportsViewportIndex;
+ public readonly bool SupportsViewportIndexVertexTessellation;
+ public readonly bool SupportsViewportMask;
public readonly bool SupportsViewportSwizzle;
public readonly bool SupportsIndirectParameters;
@@ -80,7 +81,8 @@ namespace Ryujinx.Graphics.GAL
bool supportsNonConstantTextureOffset,
bool supportsShaderBallot,
bool supportsTextureShadowLod,
- bool supportsViewportIndex,
+ bool supportsViewportIndexVertexTessellation,
+ bool supportsViewportMask,
bool supportsViewportSwizzle,
bool supportsIndirectParameters,
uint maximumUniformBuffersPerStage,
@@ -121,7 +123,8 @@ namespace Ryujinx.Graphics.GAL
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
SupportsShaderBallot = supportsShaderBallot;
SupportsTextureShadowLod = supportsTextureShadowLod;
- SupportsViewportIndex = supportsViewportIndex;
+ SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation;
+ SupportsViewportMask = supportsViewportMask;
SupportsViewportSwizzle = supportsViewportSwizzle;
SupportsIndirectParameters = supportsIndirectParameters;
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index cad2341b..78f9763f 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
- private const uint CodeGenVersion = 4707;
+ private const uint CodeGenVersion = 4565;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index bbf2702e..d35b8d92 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -144,7 +144,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
- public bool QueryHostSupportsViewportIndex() => _context.Capabilities.SupportsViewportIndex;
+ public bool QueryHostSupportsViewportIndexVertexTessellation() => _context.Capabilities.SupportsViewportIndexVertexTessellation;
+
+ public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
/// <summary>
/// Converts a packed Maxwell texture format to the shader translator texture format.
diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
index 84646526..bf365b4d 100644
--- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
+++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array"));
+ private static readonly Lazy<bool> _supportsViewportArray2 = new Lazy<bool>(() => HasExtension("GL_NV_viewport_array2"));
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
@@ -65,6 +66,7 @@ namespace Ryujinx.Graphics.OpenGL
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value;
+ public static bool SupportsViewportArray2 => _supportsViewportArray2.Value;
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
index 5a2e3fe4..3903b4d4 100644
--- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
@@ -136,7 +136,8 @@ namespace Ryujinx.Graphics.OpenGL
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
- supportsViewportIndex: HwCapabilities.SupportsShaderViewportLayerArray,
+ supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
+ supportsViewportMask: HwCapabilities.SupportsViewportArray2,
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
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);
diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
index 380c425e..c619b9bb 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
@@ -295,10 +295,12 @@ namespace Ryujinx.Graphics.Shader.Decoders
if (isStore)
{
config.SetAllOutputUserAttributes();
+ config.SetUsedFeature(FeatureFlags.OaIndexing);
}
else
{
config.SetAllInputUserAttributes();
+ config.SetUsedFeature(FeatureFlags.IaIndexing);
}
}
else
@@ -340,7 +342,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
}
if (!isStore &&
- ((attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) ||
+ (attr == AttributeConsts.FogCoord ||
+ (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) ||
(attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)))
{
config.SetUsedFeature(FeatureFlags.FixedFuncAttr);
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index bc5e67c3..2207156c 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -305,9 +305,9 @@ namespace Ryujinx.Graphics.Shader
}
/// <summary>
- /// Queries host support for writes to Layer from vertex or tessellation shader stages.
+ /// Queries host support for writes to the layer from vertex or tessellation shader stages.
/// </summary>
- /// <returns>True if writes to layer from vertex or tessellation are supported, false otherwise</returns>
+ /// <returns>True if writes to the layer from vertex or tessellation are supported, false otherwise</returns>
bool QueryHostSupportsLayerVertexTessellation()
{
return true;
@@ -350,10 +350,19 @@ namespace Ryujinx.Graphics.Shader
}
/// <summary>
- /// Queries host GPU shader viewport index output support.
+ /// Queries host support for writes to the viewport index from vertex or tessellation shader stages.
/// </summary>
- /// <returns>True if the GPU and driver supports shader viewport index output, false otherwise</returns>
- bool QueryHostSupportsViewportIndex()
+ /// <returns>True if writes to the viewport index from vertex or tessellation are supported, false otherwise</returns>
+ bool QueryHostSupportsViewportIndexVertexTessellation()
+ {
+ return true;
+ }
+
+ /// <summary>
+ /// Queries host GPU shader viewport mask output support.
+ /// </summary>
+ /// <returns>True if the GPU and driver supports shader viewport mask output, false otherwise</returns>
+ bool QueryHostSupportsViewportMask()
{
return true;
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs b/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs
new file mode 100644
index 00000000..562fb8d5
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs
@@ -0,0 +1,351 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+using System.Collections.Generic;
+
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static class AttributeMap
+ {
+ private enum StagesMask : byte
+ {
+ None = 0,
+ Compute = 1 << (int)ShaderStage.Compute,
+ Vertex = 1 << (int)ShaderStage.Vertex,
+ TessellationControl = 1 << (int)ShaderStage.TessellationControl,
+ TessellationEvaluation = 1 << (int)ShaderStage.TessellationEvaluation,
+ Geometry = 1 << (int)ShaderStage.Geometry,
+ Fragment = 1 << (int)ShaderStage.Fragment,
+
+ Tessellation = TessellationControl | TessellationEvaluation,
+ VertexTessellationGeometry = Vertex | Tessellation | Geometry,
+ TessellationGeometryFragment = Tessellation | Geometry | Fragment,
+ AllGraphics = Vertex | Tessellation | Geometry | Fragment
+ }
+
+ private struct AttributeEntry
+ {
+ public int BaseOffset { get; }
+ public AggregateType Type { get; }
+ public IoVariable IoVariable { get; }
+ public StagesMask InputMask { get; }
+ public StagesMask OutputMask { get; }
+
+ public AttributeEntry(
+ int baseOffset,
+ AggregateType type,
+ IoVariable ioVariable,
+ StagesMask inputMask,
+ StagesMask outputMask)
+ {
+ BaseOffset = baseOffset;
+ Type = type;
+ IoVariable = ioVariable;
+ InputMask = inputMask;
+ OutputMask = outputMask;
+ }
+ }
+
+ private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributes;
+ private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributesPerPatch;
+
+ static AttributeMap()
+ {
+ _attributes = CreateMap();
+ _attributesPerPatch = CreatePerPatchMap();
+ }
+
+ private static IReadOnlyDictionary<int, AttributeEntry> CreateMap()
+ {
+ var map = new Dictionary<int, AttributeEntry>();
+
+ Add(map, 0x060, AggregateType.S32, IoVariable.PrimitiveId, StagesMask.TessellationGeometryFragment, StagesMask.Geometry);
+ Add(map, 0x064, AggregateType.S32, IoVariable.Layer, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x068, AggregateType.S32, IoVariable.ViewportIndex, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x06c, AggregateType.FP32, IoVariable.PointSize, StagesMask.None, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x070, AggregateType.Vector4 | AggregateType.FP32, IoVariable.Position, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x080, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.AllGraphics, StagesMask.VertexTessellationGeometry, 32);
+ Add(map, 0x280, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x290, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x2a0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x2b0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x2c0, AggregateType.Array | AggregateType.FP32, IoVariable.ClipDistance, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry, 8);
+ Add(map, 0x2e0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.PointCoord, StagesMask.Fragment, StagesMask.None);
+ Add(map, 0x2e8, AggregateType.FP32, IoVariable.FogCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x2f0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationCoord, StagesMask.TessellationEvaluation, StagesMask.None);
+ Add(map, 0x2f8, AggregateType.S32, IoVariable.InstanceId, StagesMask.Vertex, StagesMask.None);
+ Add(map, 0x2fc, AggregateType.S32, IoVariable.VertexId, StagesMask.Vertex, StagesMask.None);
+ Add(map, 0x300, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TextureCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x3a0, AggregateType.Array | AggregateType.S32, IoVariable.ViewportMask, StagesMask.Fragment, StagesMask.VertexTessellationGeometry);
+ Add(map, 0x3fc, AggregateType.Bool, IoVariable.FrontFacing, StagesMask.Fragment, StagesMask.None);
+
+ return map;
+ }
+
+ private static IReadOnlyDictionary<int, AttributeEntry> CreatePerPatchMap()
+ {
+ var map = new Dictionary<int, AttributeEntry>();
+
+ Add(map, 0x000, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TessellationLevelOuter, StagesMask.TessellationEvaluation, StagesMask.TessellationControl);
+ Add(map, 0x010, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationLevelInner, StagesMask.TessellationEvaluation, StagesMask.TessellationControl);
+ Add(map, 0x018, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.TessellationEvaluation, StagesMask.TessellationControl, 31, 0x200);
+
+ return map;
+ }
+
+ private static void Add(
+ Dictionary<int, AttributeEntry> attributes,
+ int offset,
+ AggregateType type,
+ IoVariable ioVariable,
+ StagesMask inputMask,
+ StagesMask outputMask,
+ int count = 1,
+ int upperBound = 0x400)
+ {
+ int baseOffset = offset;
+
+ int elementsCount = GetElementCount(type);
+
+ for (int index = 0; index < count; index++)
+ {
+ for (int elementIndex = 0; elementIndex < elementsCount; elementIndex++)
+ {
+ attributes.Add(offset, new AttributeEntry(baseOffset, type, ioVariable, inputMask, outputMask));
+
+ offset += 4;
+
+ if (offset >= upperBound)
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ public static Operand GenerateAttributeLoad(EmitterContext context, Operand primVertex, int offset, bool isOutput, bool isPerPatch)
+ {
+ if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry))
+ {
+ context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid.");
+ return Const(0);
+ }
+
+ StagesMask validUseMask = isOutput ? entry.OutputMask : entry.InputMask;
+
+ if (((StagesMask)(1 << (int)context.Config.Stage) & validUseMask) == StagesMask.None)
+ {
+ context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}.");
+ return Const(0);
+ }
+
+ if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable))
+ {
+ context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}.");
+ return Const(0);
+ }
+
+ if (HasInvocationId(context.Config.Stage, isOutput) && !isPerPatch)
+ {
+ primVertex = context.Load(StorageKind.Input, IoVariable.InvocationId);
+ }
+
+ int innerOffset = offset - entry.BaseOffset;
+ int innerIndex = innerOffset / 4;
+
+ StorageKind storageKind = isPerPatch
+ ? (isOutput ? StorageKind.OutputPerPatch : StorageKind.InputPerPatch)
+ : (isOutput ? StorageKind.Output : StorageKind.Input);
+ IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry);
+ AggregateType type = GetType(context.Config, isOutput, innerIndex, in entry);
+ int elementCount = GetElementCount(type);
+
+ bool isArray = type.HasFlag(AggregateType.Array);
+ bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput);
+
+ bool hasElementIndex = elementCount > 1;
+
+ if (hasArrayIndex && hasElementIndex)
+ {
+ int arrayIndex = innerIndex / elementCount;
+ int elementIndex = innerIndex - (arrayIndex * elementCount);
+
+ return primVertex == null || isArray
+ ? context.Load(storageKind, ioVariable, primVertex, Const(arrayIndex), Const(elementIndex))
+ : context.Load(storageKind, ioVariable, Const(arrayIndex), primVertex, Const(elementIndex));
+ }
+ else if (hasArrayIndex || hasElementIndex)
+ {
+ return primVertex == null || isArray || !hasArrayIndex
+ ? context.Load(storageKind, ioVariable, primVertex, Const(innerIndex))
+ : context.Load(storageKind, ioVariable, Const(innerIndex), primVertex);
+ }
+ else
+ {
+ return context.Load(storageKind, ioVariable, primVertex);
+ }
+ }
+
+ public static void GenerateAttributeStore(EmitterContext context, int offset, bool isPerPatch, Operand value)
+ {
+ if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry))
+ {
+ context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid.");
+ return;
+ }
+
+ if (((StagesMask)(1 << (int)context.Config.Stage) & entry.OutputMask) == StagesMask.None)
+ {
+ context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}.");
+ return;
+ }
+
+ if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable))
+ {
+ context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}.");
+ return;
+ }
+
+ Operand invocationId = null;
+
+ if (HasInvocationId(context.Config.Stage, isOutput: true) && !isPerPatch)
+ {
+ invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
+ }
+
+ int innerOffset = offset - entry.BaseOffset;
+ int innerIndex = innerOffset / 4;
+
+ StorageKind storageKind = isPerPatch ? StorageKind.OutputPerPatch : StorageKind.Output;
+ IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry);
+ AggregateType type = GetType(context.Config, isOutput: true, innerIndex, in entry);
+ int elementCount = GetElementCount(type);
+
+ bool isArray = type.HasFlag(AggregateType.Array);
+ bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput: true);
+
+ bool hasElementIndex = elementCount > 1;
+
+ if (hasArrayIndex && hasElementIndex)
+ {
+ int arrayIndex = innerIndex / elementCount;
+ int elementIndex = innerIndex - (arrayIndex * elementCount);
+
+ if (invocationId == null || isArray)
+ {
+ context.Store(storageKind, ioVariable, invocationId, Const(arrayIndex), Const(elementIndex), value);
+ }
+ else
+ {
+ context.Store(storageKind, ioVariable, Const(arrayIndex), invocationId, Const(elementIndex), value);
+ }
+ }
+ else if (hasArrayIndex || hasElementIndex)
+ {
+ if (invocationId == null || isArray || !hasArrayIndex)
+ {
+ context.Store(storageKind, ioVariable, invocationId, Const(innerIndex), value);
+ }
+ else
+ {
+ context.Store(storageKind, ioVariable, Const(innerIndex), invocationId, value);
+ }
+ }
+ else
+ {
+ context.Store(storageKind, ioVariable, invocationId, value);
+ }
+ }
+
+ private static bool IsSupportedByHost(IGpuAccessor gpuAccessor, ShaderStage stage, IoVariable ioVariable)
+ {
+ if (ioVariable == IoVariable.ViewportIndex && stage != ShaderStage.Geometry && stage != ShaderStage.Fragment)
+ {
+ return gpuAccessor.QueryHostSupportsViewportIndexVertexTessellation();
+ }
+ else if (ioVariable == IoVariable.ViewportMask)
+ {
+ return gpuAccessor.QueryHostSupportsViewportMask();
+ }
+
+ return true;
+ }
+
+ public static IoVariable GetIoVariable(ShaderConfig config, int offset, out int location)
+ {
+ location = 0;
+
+ if (!_attributes.TryGetValue(offset, out AttributeEntry entry))
+ {
+ return IoVariable.Invalid;
+ }
+
+ if (((StagesMask)(1 << (int)config.Stage) & entry.OutputMask) == StagesMask.None)
+ {
+ return IoVariable.Invalid;
+ }
+
+ if (config.HasPerLocationInputOrOutput(entry.IoVariable, isOutput: true))
+ {
+ location = (offset - entry.BaseOffset) / 16;
+ }
+
+ return GetIoVariable(config.Stage, in entry);
+ }
+
+ private static IoVariable GetIoVariable(ShaderStage stage, in AttributeEntry entry)
+ {
+ if (entry.IoVariable == IoVariable.Position && stage == ShaderStage.Fragment)
+ {
+ return IoVariable.FragmentCoord;
+ }
+
+ return entry.IoVariable;
+ }
+
+ private static AggregateType GetType(ShaderConfig config, bool isOutput, int innerIndex, in AttributeEntry entry)
+ {
+ AggregateType type = entry.Type;
+
+ if (entry.IoVariable == IoVariable.UserDefined)
+ {
+ type = config.GetUserDefinedType(innerIndex / 4, isOutput);
+ }
+ else if (entry.IoVariable == IoVariable.FragmentOutputColor)
+ {
+ type = config.GetFragmentOutputColorType(innerIndex / 4);
+ }
+
+ return type;
+ }
+
+ public static bool HasPrimitiveVertex(ShaderStage stage, bool isOutput)
+ {
+ if (isOutput)
+ {
+ return false;
+ }
+
+ return stage == ShaderStage.TessellationControl ||
+ stage == ShaderStage.TessellationEvaluation ||
+ stage == ShaderStage.Geometry;
+ }
+
+ public static bool HasInvocationId(ShaderStage stage, bool isOutput)
+ {
+ return isOutput && stage == ShaderStage.TessellationControl;
+ }
+
+ private static int GetElementCount(AggregateType type)
+ {
+ return (type & AggregateType.ElementCountMask) switch
+ {
+ AggregateType.Vector2 => 2,
+ AggregateType.Vector3 => 3,
+ AggregateType.Vector4 => 4,
+ _ => 1
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
index 9f9ac141..1df38761 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
@@ -20,7 +20,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
InstAld op = context.GetOp<InstAld>();
- Operand primVertex = context.Copy(GetSrcReg(context, op.SrcB));
+ // Some of those attributes are per invocation,
+ // so we should ignore any primitive vertex indexing for those.
+ bool hasPrimitiveVertex = AttributeMap.HasPrimitiveVertex(context.Config.Stage, op.O) && !op.P;
+
+ if (!op.Phys)
+ {
+ hasPrimitiveVertex &= HasPrimitiveVertex(op.Imm11);
+ }
+
+ Operand primVertex = hasPrimitiveVertex ? context.Copy(GetSrcReg(context, op.SrcB)) : null;
for (int index = 0; index < (int)op.AlSize + 1; index++)
{
@@ -33,12 +42,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (op.Phys)
{
- Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
- Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
+ Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
+ Operand vecIndex = context.ShiftRightU32(offset, Const(4));
+ Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
- context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex));
+ StorageKind storageKind = op.O ? StorageKind.Output : StorageKind.Input;
- context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
+ context.Copy(Register(rd), context.Load(storageKind, IoVariable.UserDefined, primVertex, vecIndex, elemIndex));
}
else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P)
{
@@ -46,14 +56,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeRead(offset);
- if (op.O && CanLoadOutput(offset))
+ bool isOutput = op.O && CanLoadOutput(offset);
+
+ if (!op.P && !isOutput && TryConvertIdToIndexForVulkan(context, offset, out Operand value))
{
- offset |= AttributeConsts.LoadOutputMask;
+ context.Copy(Register(rd), value);
+ }
+ else
+ {
+ context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, op.P));
}
-
- Operand src = op.P ? AttributePerPatch(offset) : CreateInputAttribute(context, offset);
-
- context.Copy(Register(rd), src);
}
else
{
@@ -61,14 +73,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeRead(offset);
- if (op.O && CanLoadOutput(offset))
- {
- offset |= AttributeConsts.LoadOutputMask;
- }
-
- Operand src = Const(offset);
+ bool isOutput = op.O && CanLoadOutput(offset);
- context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
+ context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, false));
}
}
}
@@ -88,12 +95,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (op.Phys)
{
- Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
- Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
-
- context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd));
-
- context.Config.SetUsedFeature(FeatureFlags.OaIndexing);
+ Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
+ Operand vecIndex = context.ShiftRightU32(offset, Const(4));
+ Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
+ Operand invocationId = AttributeMap.HasInvocationId(context.Config.Stage, isOutput: true)
+ ? context.Load(StorageKind.Input, IoVariable.InvocationId)
+ : null;
+
+ context.Store(StorageKind.Output, IoVariable.UserDefined, invocationId, vecIndex, elemIndex, Register(rd));
}
else
{
@@ -110,9 +119,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.FlagAttributeWritten(offset);
- Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset);
-
- context.Copy(dest, Register(rd));
+ AttributeMap.GenerateAttributeStore(context, offset, op.P, Register(rd));
}
}
}
@@ -129,13 +136,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (op.Idx)
{
- Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
- Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
+ Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
+ Operand vecIndex = context.ShiftRightU32(offset, Const(4));
+ Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3));
- res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0));
- res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
-
- context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
+ res = context.Load(StorageKind.Input, IoVariable.UserDefined, null, vecIndex, elemIndex);
+ res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3)));
}
else
{
@@ -147,9 +153,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
{
- res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
+ res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3)));
}
}
+ else if (op.Imm10 == AttributeConsts.PositionX || op.Imm10 == AttributeConsts.PositionY)
+ {
+ // FragCoord X/Y must be divided by the render target scale, if resolution scaling is active,
+ // because the shader code is not expecting scaled values.
+ res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0)));
+ }
+ else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
+ {
+ // gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
+ // This weird trick makes it behave.
+ res = context.ICompareLess(context.INegate(context.IConvertS32ToFP32(res)), Const(0));
+ }
}
if (op.IpaOp == IpaOp.Multiply && !isFixedFunc)
@@ -216,17 +234,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (tempXLocal != null)
{
- context.Copy(Attribute(AttributeConsts.PositionX), tempXLocal);
+ context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(0)), tempXLocal);
}
if (tempYLocal != null)
{
- context.Copy(Attribute(AttributeConsts.PositionY), tempYLocal);
+ context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(1)), tempYLocal);
}
if (tempZLocal != null)
{
- context.Copy(Attribute(AttributeConsts.PositionZ), tempZLocal);
+ context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(2)), tempZLocal);
}
}
else
@@ -241,6 +259,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
+ private static bool HasPrimitiveVertex(int attr)
+ {
+ return attr != AttributeConsts.PrimitiveId &&
+ attr != AttributeConsts.TessCoordX &&
+ attr != AttributeConsts.TessCoordY;
+ }
+
private static bool CanLoadOutput(int attr)
{
return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY;
@@ -252,13 +277,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
// TODO: If two sided rendering is enabled, then this should return
// FrontColor if the fragment is front facing, and back color otherwise.
- int index = (attr - AttributeConsts.FrontColorDiffuseR) >> 4;
- int userAttrIndex = context.Config.GetFreeUserAttribute(isOutput: false, index);
- Operand frontAttr = Attribute(AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf));
-
- context.Config.SetInputUserAttributeFixedFunc(userAttrIndex);
-
- selectedAttr = frontAttr;
+ selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
+ return true;
+ }
+ else if (attr == AttributeConsts.FogCoord)
+ {
+ // TODO: We likely need to emulate the fixed-function functionality for FogCoord here.
+ selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
return true;
}
else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0)
@@ -268,14 +293,19 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
{
- selectedAttr = Attribute(FixedFuncToUserAttribute(context.Config, attr, AttributeConsts.TexCoordBase, 4, isOutput: false));
+ selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false));
return true;
}
- selectedAttr = Attribute(attr);
+ selectedAttr = GenerateIpaLoad(context, attr);
return false;
}
+ private static Operand GenerateIpaLoad(EmitterContext context, int offset)
+ {
+ return AttributeMap.GenerateAttributeLoad(context, null, offset, isOutput: false, isPerPatch: false);
+ }
+
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
{
bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
@@ -286,13 +316,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput);
config.SetLayerOutputAttribute(attr);
}
+ else if (attr == AttributeConsts.FogCoord)
+ {
+ attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FogCoord, fixedStartAttr, isOutput);
+ }
else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
{
- attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr, isOutput);
+ attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr + 1, isOutput);
}
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
{
- attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 4, isOutput);
+ attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 5, isOutput);
}
return attr;
@@ -301,11 +335,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput)
{
int index = (attr - baseAttr) >> 4;
- int userAttrIndex = config.GetFreeUserAttribute(isOutput, index);
+ int userAttrIndex = config.GetFreeUserAttribute(isOutput, baseIndex + index);
if ((uint)userAttrIndex < Constants.MaxAttributes)
{
- userAttrIndex += baseIndex;
attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf);
if (isOutput)
@@ -317,25 +350,34 @@ namespace Ryujinx.Graphics.Shader.Instructions
config.SetInputUserAttributeFixedFunc(userAttrIndex);
}
}
+ else
+ {
+ config.GpuAccessor.Log($"No enough user attributes for fixed attribute offset 0x{attr:X}.");
+ }
return attr;
}
- private static Operand CreateInputAttribute(EmitterContext context, int attr)
+ private static bool TryConvertIdToIndexForVulkan(EmitterContext context, int attr, out Operand value)
{
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
if (attr == AttributeConsts.InstanceId)
{
- return context.ISubtract(Attribute(AttributeConsts.InstanceIndex), Attribute(AttributeConsts.BaseInstance));
+ value = context.ISubtract(
+ context.Load(StorageKind.Input, IoVariable.InstanceIndex),
+ context.Load(StorageKind.Input, IoVariable.BaseInstance));
+ return true;
}
else if (attr == AttributeConsts.VertexId)
{
- return Attribute(AttributeConsts.VertexIndex);
+ value = context.Load(StorageKind.Input, IoVariable.VertexIndex);
+ return true;
}
}
- return Attribute(attr);
+ value = null;
+ return false;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index ceb76de1..c73c6b2a 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand value = GetSrcReg(context, op.SrcB);
- Operand res = EmitAtomicOp(context, Instruction.MrGlobal, op.Op, op.Size, addrLow, addrHigh, value);
+ Operand res = EmitAtomicOp(context, StorageKind.GlobalMemory, op.Op, op.Size, addrLow, addrHigh, value);
context.Copy(GetDest(op.Dest), res);
}
@@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
_ => AtomSize.U32
};
- Operand res = EmitAtomicOp(context, Instruction.MrShared, op.AtomOp, size, offset, Const(0), value);
+ Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, offset, Const(0), value);
context.Copy(GetDest(op.Dest), res);
}
@@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(op.SrcA, RegisterType.Gpr), op.E, op.Imm20);
- EmitAtomicOp(context, Instruction.MrGlobal, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB));
+ EmitAtomicOp(context, StorageKind.GlobalMemory, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB));
}
public static void Stg(EmitterContext context)
@@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
private static Operand EmitAtomicOp(
EmitterContext context,
- Instruction mr,
+ StorageKind storageKind,
AtomOp op,
AtomSize type,
Operand addrLow,
@@ -170,7 +170,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
case AtomOp.Add:
if (type == AtomSize.S32 || type == AtomSize.U32)
{
- res = context.AtomicAdd(mr, addrLow, addrHigh, value);
+ res = context.AtomicAdd(storageKind, addrLow, addrHigh, value);
}
else
{
@@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
case AtomOp.And:
if (type == AtomSize.S32 || type == AtomSize.U32)
{
- res = context.AtomicAnd(mr, addrLow, addrHigh, value);
+ res = context.AtomicAnd(storageKind, addrLow, addrHigh, value);
}
else
{
@@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
case AtomOp.Xor:
if (type == AtomSize.S32 || type == AtomSize.U32)
{
- res = context.AtomicXor(mr, addrLow, addrHigh, value);
+ res = context.AtomicXor(storageKind, addrLow, addrHigh, value);
}
else
{
@@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
case AtomOp.Or:
if (type == AtomSize.S32 || type == AtomSize.U32)
{
- res = context.AtomicOr(mr, addrLow, addrHigh, value);
+ res = context.AtomicOr(storageKind, addrLow, addrHigh, value);
}
else
{
@@ -210,11 +210,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
case AtomOp.Max:
if (type == AtomSize.S32)
{
- res = context.AtomicMaxS32(mr, addrLow, addrHigh, value);
+ res = context.AtomicMaxS32(storageKind, addrLow, addrHigh, value);
}
else if (type == AtomSize.U32)
{
- res = context.AtomicMaxU32(mr, addrLow, addrHigh, value);
+ res = context.AtomicMaxU32(storageKind, addrLow, addrHigh, value);
}
else
{
@@ -224,11 +224,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
case AtomOp.Min:
if (type == AtomSize.S32)
{
- res = context.AtomicMinS32(mr, addrLow, addrHigh, value);
+ res = context.AtomicMinS32(storageKind, addrLow, addrHigh, value);
}
else if (type == AtomSize.U32)
{
- res = context.AtomicMinU32(mr, addrLow, addrHigh, value);
+ res = context.AtomicMinU32(storageKind, addrLow, addrHigh, value);
}
else
{
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
index 16b02f97..9992ac37 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
@@ -76,11 +76,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
switch (op.SReg)
{
case SReg.LaneId:
- src = Attribute(AttributeConsts.LaneId);
+ src = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
break;
case SReg.InvocationId:
- src = Attribute(AttributeConsts.InvocationId);
+ src = context.Load(StorageKind.Input, IoVariable.InvocationId);
break;
case SReg.YDirection:
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
break;
case SReg.ThreadKill:
- src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0);
+ src = context.Config.Stage == ShaderStage.Fragment ? context.Load(StorageKind.Input, IoVariable.ThreadKill) : Const(0);
break;
case SReg.InvocationInfo:
@@ -101,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (context.Config.Stage == ShaderStage.TessellationControl ||
context.Config.Stage == ShaderStage.TessellationEvaluation)
{
- src = context.ShiftLeft(Attribute(AttributeConsts.PatchVerticesIn), Const(16));
+ src = context.ShiftLeft(context.Load(StorageKind.Input, IoVariable.PatchVertices), Const(16));
}
else
{
@@ -115,9 +115,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
break;
case SReg.TId:
- Operand tidX = Attribute(AttributeConsts.ThreadIdX);
- Operand tidY = Attribute(AttributeConsts.ThreadIdY);
- Operand tidZ = Attribute(AttributeConsts.ThreadIdZ);
+ Operand tidX = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(0));
+ Operand tidY = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(1));
+ Operand tidZ = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(2));
tidY = context.ShiftLeft(tidY, Const(16));
tidZ = context.ShiftLeft(tidZ, Const(26));
@@ -126,39 +126,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
break;
case SReg.TIdX:
- src = Attribute(AttributeConsts.ThreadIdX);
+ src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(0));
break;
case SReg.TIdY:
- src = Attribute(AttributeConsts.ThreadIdY);
+ src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(1));
break;
case SReg.TIdZ:
- src = Attribute(AttributeConsts.ThreadIdZ);
+ src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(2));
break;
case SReg.CtaIdX:
- src = Attribute(AttributeConsts.CtaIdX);
+ src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(0));
break;
case SReg.CtaIdY:
- src = Attribute(AttributeConsts.CtaIdY);
+ src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(1));
break;
case SReg.CtaIdZ:
- src = Attribute(AttributeConsts.CtaIdZ);
+ src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(2));
break;
case SReg.EqMask:
- src = Attribute(AttributeConsts.EqMask);
+ src = context.Load(StorageKind.Input, IoVariable.SubgroupEqMask, null, Const(0));
break;
case SReg.LtMask:
- src = Attribute(AttributeConsts.LtMask);
+ src = context.Load(StorageKind.Input, IoVariable.SubgroupLtMask, null, Const(0));
break;
case SReg.LeMask:
- src = Attribute(AttributeConsts.LeMask);
+ src = context.Load(StorageKind.Input, IoVariable.SubgroupLeMask, null, Const(0));
break;
case SReg.GtMask:
- src = Attribute(AttributeConsts.GtMask);
+ src = context.Load(StorageKind.Input, IoVariable.SubgroupGtMask, null, Const(0));
break;
case SReg.GeMask:
- src = Attribute(AttributeConsts.GeMask);
+ src = context.Load(StorageKind.Input, IoVariable.SubgroupGeMask, null, Const(0));
break;
default:
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
index aa9776bc..d7c4a961 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
@@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
ImageStore,
ImageAtomic,
IsNan,
- LoadAttribute,
+ Load,
LoadConstant,
LoadGlobal,
LoadLocal,
@@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
ShuffleXor,
Sine,
SquareRoot,
- StoreAttribute,
+ Store,
StoreGlobal,
StoreGlobal16,
StoreGlobal8,
@@ -144,13 +144,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
FP32 = 1 << 16,
FP64 = 1 << 17,
- MrShift = 18,
-
- MrGlobal = 0 << MrShift,
- MrShared = 1 << MrShift,
- MrStorage = 2 << MrShift,
- MrMask = 3 << MrShift,
-
Mask = 0xffff
}
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs
new file mode 100644
index 00000000..a2163d14
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs
@@ -0,0 +1,51 @@
+namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
+{
+ enum IoVariable
+ {
+ Invalid,
+
+ BackColorDiffuse,
+ BackColorSpecular,
+ BaseInstance,
+ BaseVertex,
+ ClipDistance,
+ CtaId,
+ DrawIndex,
+ FogCoord,
+ FragmentCoord,
+ FragmentOutputColor,
+ FragmentOutputDepth,
+ FragmentOutputIsBgra, // TODO: Remove and use constant buffer access.
+ FrontColorDiffuse,
+ FrontColorSpecular,
+ FrontFacing,
+ InstanceId,
+ InstanceIndex,
+ InvocationId,
+ Layer,
+ PatchVertices,
+ PointCoord,
+ PointSize,
+ Position,
+ PrimitiveId,
+ SubgroupEqMask,
+ SubgroupGeMask,
+ SubgroupGtMask,
+ SubgroupLaneId,
+ SubgroupLeMask,
+ SubgroupLtMask,
+ SupportBlockViewInverse, // TODO: Remove and use constant buffer access.
+ SupportBlockRenderScale, // TODO: Remove and use constant buffer access.
+ TessellationCoord,
+ TessellationLevelInner,
+ TessellationLevelOuter,
+ TextureCoord,
+ ThreadId,
+ ThreadKill,
+ UserDefined,
+ VertexId,
+ VertexIndex,
+ ViewportIndex,
+ ViewportMask
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs
index 7fed861e..37c349e8 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs
@@ -10,16 +10,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
return new Operand(OperandType.Argument, value);
}
- public static Operand Attribute(int value)
- {
- return new Operand(OperandType.Attribute, value);
- }
-
- public static Operand AttributePerPatch(int value)
- {
- return new Operand(OperandType.AttributePerPatch, value);
- }
-
public static Operand Cbuf(int slot, int offset)
{
return new Operand(slot, offset);
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs
index 7566a03f..4d2da734 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs
@@ -3,8 +3,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
enum OperandType
{
Argument,
- Attribute,
- AttributePerPatch,
Constant,
ConstantBuffer,
Label,
@@ -12,12 +10,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Register,
Undefined
}
-
- static class OperandTypeExtensions
- {
- public static bool IsAttribute(this OperandType type)
- {
- return type == OperandType.Attribute || type == OperandType.AttributePerPatch;
- }
- }
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
index 18e203a7..99179f15 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
@@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
class Operation : INode
{
public Instruction Inst { get; private set; }
+ public StorageKind StorageKind { get; }
private Operand[] _dests;
@@ -99,6 +100,23 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
}
}
+ public Operation(Instruction inst, StorageKind storageKind, Operand dest, params Operand[] sources) : this(sources)
+ {
+ Inst = inst;
+ StorageKind = storageKind;
+
+ if (dest != null)
+ {
+ dest.AsgOp = this;
+
+ _dests = new[] { dest };
+ }
+ else
+ {
+ _dests = Array.Empty<Operand>();
+ }
+ }
+
public Operation(Instruction inst, int index, Operand dest, params Operand[] sources) : this(inst, dest, sources)
{
Index = index;
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs
new file mode 100644
index 00000000..59357443
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs
@@ -0,0 +1,39 @@
+namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
+{
+ enum StorageKind
+ {
+ None,
+ Input,
+ InputPerPatch,
+ Output,
+ OutputPerPatch,
+ ConstantBuffer,
+ StorageBuffer,
+ LocalMemory,
+ SharedMemory,
+ GlobalMemory
+ }
+
+ static class StorageKindExtensions
+ {
+ public static bool IsInputOrOutput(this StorageKind storageKind)
+ {
+ return storageKind == StorageKind.Input ||
+ storageKind == StorageKind.InputPerPatch ||
+ storageKind == StorageKind.Output ||
+ storageKind == StorageKind.OutputPerPatch;
+ }
+
+ public static bool IsOutput(this StorageKind storageKind)
+ {
+ return storageKind == StorageKind.Output ||
+ storageKind == StorageKind.OutputPerPatch;
+ }
+
+ public static bool IsPerPatch(this StorageKind storageKind)
+ {
+ return storageKind == StorageKind.InputPerPatch ||
+ storageKind == StorageKind.OutputPerPatch;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs
index 19397256..2393fd8d 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
class AstOperation : AstNode
{
public Instruction Inst { get; }
+ public StorageKind StorageKind { get; }
public int Index { get; }
@@ -16,9 +17,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public int SourcesCount => _sources.Length;
- public AstOperation(Instruction inst, IAstNode[] sources, int sourcesCount)
+ public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount)
{
- Inst = inst;
+ Inst = inst;
+ StorageKind = storageKind;
_sources = sources;
for (int index = 0; index < sources.Length; index++)
@@ -36,12 +38,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Index = 0;
}
- public AstOperation(Instruction inst, int index, IAstNode[] sources, int sourcesCount) : this(inst, sources, sourcesCount)
+ public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount)
{
Index = index;
}
- public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, sources, sources.Length)
+ public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length)
{
}
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
index 957a956f..a44f13cc 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
int cbufSlot,
int handle,
int index,
- params IAstNode[] sources) : base(inst, index, sources, sources.Length)
+ params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length)
{
Type = type;
Format = format;
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
index 0a9a9e51..8eccef23 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
@@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.ImageStore, AggregateType.Void);
Add(Instruction.ImageAtomic, AggregateType.S32);
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
- Add(Instruction.LoadAttribute, AggregateType.FP32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
+ Add(Instruction.Load, AggregateType.FP32);
Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
- Add(Instruction.StoreAttribute, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.FP32);
+ Add(Instruction.Store, AggregateType.Void);
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
return AggregateType.FP32;
}
- else if (inst == Instruction.Call)
+ else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store)
{
return AggregateType.S32;
}
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs b/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs
new file mode 100644
index 00000000..21a1b3f0
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs
@@ -0,0 +1,44 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System;
+
+namespace Ryujinx.Graphics.Shader.StructuredIr
+{
+ readonly struct IoDefinition : IEquatable<IoDefinition>
+ {
+ public StorageKind StorageKind { get; }
+ public IoVariable IoVariable { get; }
+ public int Location { get; }
+ public int Component { get; }
+
+ public IoDefinition(StorageKind storageKind, IoVariable ioVariable, int location = 0, int component = 0)
+ {
+ StorageKind = storageKind;
+ IoVariable = ioVariable;
+ Location = location;
+ Component = component;
+ }
+
+ public override bool Equals(object other)
+ {
+ return other is IoDefinition ioDefinition && Equals(ioDefinition);
+ }
+
+ public bool Equals(IoDefinition other)
+ {
+ return StorageKind == other.StorageKind &&
+ IoVariable == other.IoVariable &&
+ Location == other.Location &&
+ Component == other.Component;
+ }
+
+ public override int GetHashCode()
+ {
+ return (int)StorageKind | ((int)IoVariable << 8) | (Location << 16) | (Component << 24);
+ }
+
+ public override string ToString()
+ {
+ return $"{StorageKind}.{IoVariable}.{Location}.{Component}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
index 730468a4..38ed1584 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
@@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return type switch
{
OperandType.Argument => AggregateType.S32,
- OperandType.Attribute => AggregateType.FP32,
- OperandType.AttributePerPatch => AggregateType.FP32,
OperandType.Constant => AggregateType.S32,
OperandType.ConstantBuffer => AggregateType.FP32,
OperandType.Undefined => AggregateType.S32,
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
index b8d38fa6..b4ca8ee5 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
@@ -65,49 +65,35 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
context.LeaveFunction();
}
- if (config.TransformFeedbackEnabled && (config.LastInVertexPipeline || config.Stage == ShaderStage.Fragment))
- {
- for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
- {
- var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
- var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
-
- for (int i = 0; i < locations.Length; i++)
- {
- byte location = locations[i];
- if (location < 0xc0)
- {
- context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
- }
- }
- }
- }
-
return context.Info;
}
private static void AddOperation(StructuredProgramContext context, Operation operation)
{
Instruction inst = operation.Inst;
+ StorageKind storageKind = operation.StorageKind;
- if (inst == Instruction.LoadAttribute)
+ if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput())
{
- Operand src1 = operation.GetSource(0);
- Operand src2 = operation.GetSource(1);
+ IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
+ bool isOutput = storageKind.IsOutput();
+ bool perPatch = storageKind.IsPerPatch();
+ int location = 0;
+ int component = 0;
- if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant)
+ if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
{
- int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2);
+ location = operation.GetSource(1).Value;
- if ((src1.Value & AttributeConsts.LoadOutputMask) != 0)
+ if (operation.SourcesCount > 2 &&
+ operation.GetSource(2).Type == OperandType.Constant &&
+ context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
{
- context.Info.Outputs.Add(attrOffset);
- }
- else
- {
- context.Info.Inputs.Add(attrOffset);
+ component = operation.GetSource(2).Value;
}
}
+
+ context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
}
bool vectorDest = IsVectorDestInst(inst);
@@ -119,12 +105,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
for (int index = 0; index < operation.SourcesCount; index++)
{
- sources[index] = context.GetOperandUse(operation.GetSource(index));
+ sources[index] = context.GetOperand(operation.GetSource(index));
}
for (int index = 0; index < outDestsCount; index++)
{
- AstOperand oper = context.GetOperandDef(operation.GetDest(1 + index));
+ AstOperand oper = context.GetOperand(operation.GetDest(1 + index));
oper.VarType = InstructionInfo.GetSrcVarType(inst, sourcesCount + index);
@@ -163,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else
{
- source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
+ source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
}
AggregateType destElemType = destType;
@@ -181,17 +167,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
for (int i = 0; i < operation.DestsCount; i++)
{
- AstOperand dest = context.GetOperandDef(operation.GetDest(i));
+ AstOperand dest = context.GetOperand(operation.GetDest(i));
AstOperand index = new AstOperand(OperandType.Constant, i);
dest.VarType = destElemType;
- context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, new[] { destVec, index }, 2)));
+ context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2)));
}
}
else if (operation.Dest != null)
{
- AstOperand dest = context.GetOperandDef(operation.Dest);
+ AstOperand dest = context.GetOperand(operation.Dest);
// If all the sources are bool, it's better to use short-circuiting
// logical operations, rather than forcing a cast to int and doing
@@ -234,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else if (!isCopy)
{
- source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount);
+ source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
}
else
{
@@ -255,7 +241,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else
{
- context.AddNode(new AstOperation(inst, operation.Index, sources, operation.SourcesCount));
+ context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount));
}
// Those instructions needs to be emulated by using helper functions,
@@ -263,13 +249,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
// decide which helper functions are needed on the final generated code.
switch (operation.Inst)
{
- case Instruction.AtomicMaxS32 | Instruction.MrShared:
- case Instruction.AtomicMinS32 | Instruction.MrShared:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
- break;
- case Instruction.AtomicMaxS32 | Instruction.MrStorage:
- case Instruction.AtomicMinS32 | Instruction.MrStorage:
- context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
+ case Instruction.AtomicMaxS32:
+ case Instruction.AtomicMinS32:
+ if (operation.StorageKind == StorageKind.SharedMemory)
+ {
+ context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
+ }
+ else if (operation.StorageKind == StorageKind.StorageBuffer)
+ {
+ context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
+ }
break;
case Instruction.MultiplyHighS32:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
index ce57a578..68bbdeb1 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
@@ -37,43 +37,26 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Config = config;
- if (config.Stage == ShaderStage.TessellationControl)
- {
- // Required to index outputs.
- Info.Inputs.Add(AttributeConsts.InvocationId);
- }
- else if (config.GpPassthrough)
+ if (config.GpPassthrough)
{
int passthroughAttributes = config.PassthroughAttributes;
while (passthroughAttributes != 0)
{
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
- int attrBase = AttributeConsts.UserAttributeBase + index * 16;
- Info.Inputs.Add(attrBase);
- Info.Inputs.Add(attrBase + 4);
- Info.Inputs.Add(attrBase + 8);
- Info.Inputs.Add(attrBase + 12);
+ Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.UserDefined, index));
passthroughAttributes &= ~(1 << index);
}
- Info.Inputs.Add(AttributeConsts.PositionX);
- Info.Inputs.Add(AttributeConsts.PositionY);
- Info.Inputs.Add(AttributeConsts.PositionZ);
- Info.Inputs.Add(AttributeConsts.PositionW);
- Info.Inputs.Add(AttributeConsts.PointSize);
-
- for (int i = 0; i < 8; i++)
- {
- Info.Inputs.Add(AttributeConsts.ClipDistance0 + i * 4);
- }
+ Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.Position));
+ Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.PointSize));
+ Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.ClipDistance));
}
else if (config.Stage == ShaderStage.Fragment)
{
// Potentially used for texture coordinate scaling.
- Info.Inputs.Add(AttributeConsts.PositionX);
- Info.Inputs.Add(AttributeConsts.PositionY);
+ Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord));
}
}
@@ -281,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
else
{
- cond = GetOperandUse(branchOp.GetSource(0));
+ cond = GetOperand(branchOp.GetSource(0));
Instruction invInst = type == AstBlockType.If
? Instruction.BranchIfTrue
@@ -315,41 +298,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return newTemp;
}
- public AstOperand GetOperandDef(Operand operand)
- {
- if (operand.Type == OperandType.Attribute)
- {
- Info.Outputs.Add(operand.Value & AttributeConsts.Mask);
- }
- else if (operand.Type == OperandType.AttributePerPatch)
- {
- Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask);
- }
-
- return GetOperand(operand);
- }
-
- public AstOperand GetOperandUse(Operand operand)
- {
- // If this flag is set, we're reading from an output attribute instead.
- if (operand.Type.IsAttribute() && (operand.Value & AttributeConsts.LoadOutputMask) != 0)
- {
- return GetOperandDef(operand);
- }
-
- if (operand.Type == OperandType.Attribute)
- {
- Info.Inputs.Add(operand.Value);
- }
- else if (operand.Type == OperandType.AttributePerPatch)
- {
- Info.InputsPerPatch.Add(operand.Value);
- }
-
- return GetOperand(operand);
- }
-
- private AstOperand GetOperand(Operand operand)
+ public AstOperand GetOperand(Operand operand)
{
if (operand == null)
{
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
index 489a5910..c5104146 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
@@ -22,60 +22,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
public List<StructuredFunction> Functions { get; }
- public HashSet<int> Inputs { get; }
- public HashSet<int> Outputs { get; }
- public HashSet<int> InputsPerPatch { get; }
- public HashSet<int> OutputsPerPatch { get; }
+ public HashSet<IoDefinition> IoDefinitions { get; }
public HelperFunctionsMask HelperFunctionsMask { get; set; }
- public TransformFeedbackOutput[] TransformFeedbackOutputs { get; }
-
public StructuredProgramInfo()
{
Functions = new List<StructuredFunction>();
- Inputs = new HashSet<int>();
- Outputs = new HashSet<int>();
- InputsPerPatch = new HashSet<int>();
- OutputsPerPatch = new HashSet<int>();
-
- TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
- }
-
- public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
- {
- int index = attr / 4;
- return TransformFeedbackOutputs[index];
- }
-
- public int GetTransformFeedbackOutputComponents(int attr)
- {
- int index = attr / 4;
- int baseIndex = index & ~3;
-
- int count = 1;
-
- for (; count < 4; count++)
- {
- ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1];
- ref var curr = ref TransformFeedbackOutputs[baseIndex + count];
-
- int prevOffset = prev.Offset;
- int currOffset = curr.Offset;
-
- if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
- {
- break;
- }
- }
-
- if (baseIndex + count <= index)
- {
- return 1;
- }
-
- return count;
+ IoDefinitions = new HashSet<IoDefinition>();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
index 08efbc9f..683b0d8a 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
@@ -2,104 +2,35 @@ namespace Ryujinx.Graphics.Shader.Translation
{
static class AttributeConsts
{
- public const int TessLevelOuter0 = 0x000;
- public const int TessLevelOuter1 = 0x004;
- public const int TessLevelOuter2 = 0x008;
- public const int TessLevelOuter3 = 0x00c;
- public const int TessLevelInner0 = 0x010;
- public const int TessLevelInner1 = 0x014;
- public const int PrimitiveId = 0x060;
- public const int Layer = 0x064;
- public const int ViewportIndex = 0x068;
- public const int PointSize = 0x06c;
- public const int PositionX = 0x070;
- public const int PositionY = 0x074;
- public const int PositionZ = 0x078;
- public const int PositionW = 0x07c;
- public const int FrontColorDiffuseR = 0x280;
- public const int FrontColorDiffuseG = 0x284;
- public const int FrontColorDiffuseB = 0x288;
- public const int FrontColorDiffuseA = 0x28c;
- public const int FrontColorSpecularR = 0x290;
- public const int FrontColorSpecularG = 0x294;
- public const int FrontColorSpecularB = 0x298;
- public const int FrontColorSpecularA = 0x29c;
- public const int BackColorDiffuseR = 0x2a0;
- public const int BackColorDiffuseG = 0x2a4;
- public const int BackColorDiffuseB = 0x2a8;
- public const int BackColorDiffuseA = 0x2ac;
- public const int BackColorSpecularR = 0x2b0;
- public const int BackColorSpecularG = 0x2b4;
- public const int BackColorSpecularB = 0x2b8;
- public const int BackColorSpecularA = 0x2bc;
- public const int ClipDistance0 = 0x2c0;
- public const int ClipDistance1 = 0x2c4;
- public const int ClipDistance2 = 0x2c8;
- public const int ClipDistance3 = 0x2cc;
- public const int ClipDistance4 = 0x2d0;
- public const int ClipDistance5 = 0x2d4;
- public const int ClipDistance6 = 0x2d8;
- public const int ClipDistance7 = 0x2dc;
- public const int PointCoordX = 0x2e0;
- public const int PointCoordY = 0x2e4;
- public const int TessCoordX = 0x2f0;
- public const int TessCoordY = 0x2f4;
- public const int InstanceId = 0x2f8;
- public const int VertexId = 0x2fc;
- public const int TexCoordCount = 10;
- public const int TexCoordBase = 0x300;
- public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16;
- public const int FrontFacing = 0x3fc;
+ public const int PrimitiveId = 0x060;
+ public const int Layer = 0x064;
+ public const int PositionX = 0x070;
+ public const int PositionY = 0x074;
+ public const int FrontColorDiffuseR = 0x280;
+ public const int BackColorDiffuseR = 0x2a0;
+ public const int ClipDistance0 = 0x2c0;
+ public const int ClipDistance1 = 0x2c4;
+ public const int ClipDistance2 = 0x2c8;
+ public const int ClipDistance3 = 0x2cc;
+ public const int ClipDistance4 = 0x2d0;
+ public const int ClipDistance5 = 0x2d4;
+ public const int ClipDistance6 = 0x2d8;
+ public const int ClipDistance7 = 0x2dc;
+ public const int FogCoord = 0x2e8;
+ public const int TessCoordX = 0x2f0;
+ public const int TessCoordY = 0x2f4;
+ public const int InstanceId = 0x2f8;
+ public const int VertexId = 0x2fc;
+ public const int TexCoordCount = 10;
+ public const int TexCoordBase = 0x300;
+ public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16;
+ public const int FrontFacing = 0x3fc;
public const int UserAttributesCount = 32;
- public const int UserAttributeBase = 0x80;
- public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
+ public const int UserAttributeBase = 0x80;
+ public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
public const int UserAttributePerPatchBase = 0x18;
- public const int UserAttributePerPatchEnd = 0x200;
-
- public const int LoadOutputMask = 1 << 30;
- public const int Mask = 0x3fffffff;
-
-
- // Note: Those attributes are used internally by the translator
- // only, they don't exist on Maxwell.
- public const int SpecialMask = 0xf << 24;
- public const int FragmentOutputDepth = 0x1000000;
- public const int FragmentOutputColorBase = 0x1000010;
- public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
-
- public const int FragmentOutputIsBgraBase = 0x1000100;
- public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
-
- public const int SupportBlockViewInverseX = 0x1000200;
- public const int SupportBlockViewInverseY = 0x1000204;
-
- public const int ThreadIdX = 0x2000000;
- public const int ThreadIdY = 0x2000004;
- public const int ThreadIdZ = 0x2000008;
-
- public const int CtaIdX = 0x2000010;
- public const int CtaIdY = 0x2000014;
- public const int CtaIdZ = 0x2000018;
-
- public const int LaneId = 0x2000020;
-
- public const int InvocationId = 0x2000024;
- public const int PatchVerticesIn = 0x2000028;
-
- public const int EqMask = 0x2000030;
- public const int GeMask = 0x2000034;
- public const int GtMask = 0x2000038;
- public const int LeMask = 0x200003c;
- public const int LtMask = 0x2000040;
-
- public const int ThreadKill = 0x2000044;
-
- public const int BaseInstance = 0x2000050;
- public const int BaseVertex = 0x2000054;
- public const int InstanceIndex = 0x2000058;
- public const int VertexIndex = 0x200005c;
- public const int DrawIndex = 0x2000060;
+ public const int UserAttributePerPatchEnd = 0x200;
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
deleted file mode 100644
index b671429a..00000000
--- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Shader.Translation
-{
- readonly struct AttributeInfo
- {
- private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
- {
- { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
- { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
- { AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
- { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) },
- { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
- { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
- { AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
- { AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
- { AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
- { AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
- { AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
- { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
-
- // Special.
- { AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
- { AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) },
- { AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
- { AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
- { AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
- { AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
- { AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
- { AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
- { AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) },
- { AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) },
- { AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) },
- { AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) },
- { AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
- { AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
- { AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
- { AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
- { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
- };
-
- private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
- {
- { AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
- { AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
- };
-
- public int BaseValue { get; }
- public int Value { get; }
- public int Length { get; }
- public AggregateType Type { get; }
- public bool IsBuiltin { get; }
- public bool IsValid => Type != AggregateType.Invalid;
-
- public AttributeInfo(int baseValue, int index, int length, AggregateType type, bool isBuiltin = true)
- {
- BaseValue = baseValue;
- Value = baseValue + index * 4;
- Length = length;
- Type = type;
- IsBuiltin = isBuiltin;
- }
-
- public int GetInnermostIndex()
- {
- return (Value - BaseValue) / 4;
- }
-
- public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch)
- {
- return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr);
- }
-
- public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
- {
- if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
- {
- return false;
- }
-
- return From(config, value, isOutAttr).IsValid;
- }
-
- public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr)
- {
- return FromPatch(config, value, isOutAttr).IsValid;
- }
-
- public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
- {
- value &= ~3;
-
- if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
- {
- int location = (value - AttributeConsts.UserAttributeBase) / 16;
-
- AggregateType elemType;
-
- if (config.Stage == ShaderStage.Vertex && !isOutAttr)
- {
- elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType();
- }
- else
- {
- elemType = AggregateType.FP32;
- }
-
- return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
- }
- else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
- {
- int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
- var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
- {
- AttributeType.Sint => AggregateType.S32,
- AttributeType.Uint => AggregateType.U32,
- _ => AggregateType.FP32
- };
-
- return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
- }
- else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
- {
- return new AttributeInfo(value, 0, 1, AggregateType.FP32);
- }
- else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info))
- {
- return info;
- }
-
- return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
- }
-
- public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr)
- {
- value &= ~3;
-
- if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
- {
- int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
- return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false);
- }
- else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
- {
- return info;
- }
-
- return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
- }
-
- public static bool IsArrayBuiltIn(int attr)
- {
- if (attr <= AttributeConsts.TessLevelInner1 ||
- attr == AttributeConsts.TessCoordX ||
- attr == AttributeConsts.TessCoordY)
- {
- return false;
- }
-
- return (attr & AttributeConsts.SpecialMask) == 0;
- }
-
- public static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr)
- {
- if (isOutAttr)
- {
- return stage == ShaderStage.TessellationControl;
- }
- else
- {
- return stage == ShaderStage.TessellationControl ||
- stage == ShaderStage.TessellationEvaluation ||
- stage == ShaderStage.Geometry;
- }
- }
-
- public static bool IsArrayAttributeSpirv(ShaderStage stage, bool isOutAttr)
- {
- if (isOutAttr)
- {
- return false;
- }
- else
- {
- return stage == ShaderStage.TessellationControl ||
- stage == ShaderStage.TessellationEvaluation ||
- stage == ShaderStage.Geometry;
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index 8f33cced..e81f6425 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Shader.Translation
(Config.Options.Flags & TranslationFlags.VertexA) == 0)
{
// Vulkan requires the point size to be always written on the shader if the primitive topology is points.
- this.Copy(Attribute(AttributeConsts.PointSize), ConstF(Config.GpuAccessor.QueryPointSize()));
+ this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(Config.GpuAccessor.QueryPointSize()));
}
}
@@ -87,6 +87,15 @@ namespace Ryujinx.Graphics.Shader.Translation
return dest;
}
+ public Operand Add(Instruction inst, StorageKind storageKind, Operand dest = null, params Operand[] sources)
+ {
+ Operation operation = new Operation(inst, storageKind, dest, sources);
+
+ _operations.Add(operation);
+
+ return dest;
+ }
+
public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources)
{
Operand[] dests = new[] { dest.Item1, dest.Item2 };
@@ -223,30 +232,35 @@ namespace Ryujinx.Graphics.Shader.Translation
{
if (Config.GpuAccessor.QueryViewportTransformDisable())
{
- Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask);
- Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask);
- Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX);
- Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY);
+ Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
+ Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
+ Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0));
+ Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1));
Operand negativeOne = ConstF(-1.0f);
- this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
- this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
}
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
{
- Operand z = Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask);
- Operand w = Attribute(AttributeConsts.PositionW | AttributeConsts.LoadOutputMask);
+ Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
+ Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
Operand halfW = this.FPMultiply(w, ConstF(0.5f));
- this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
}
if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute)
{
Config.SetUsedFeature(FeatureFlags.RtLayer);
- this.Copy(Attribute(AttributeConsts.Layer), Attribute(Config.GpLayerInputAttribute | AttributeConsts.LoadOutputMask));
+ int attrVecIndex = Config.GpLayerInputAttribute >> 2;
+ int attrComponentIndex = Config.GpLayerInputAttribute & 3;
+
+ Operand layer = this.Load(StorageKind.Output, IoVariable.UserDefined, null, Const(attrVecIndex), Const(attrComponentIndex));
+
+ this.Store(StorageKind.Output, IoVariable.Layer, null, layer);
}
}
@@ -255,9 +269,9 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Config.GpuAccessor.QueryViewportTransformDisable())
{
oldXLocal = Local();
- this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask));
+ this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)));
oldYLocal = Local();
- this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask));
+ this.Copy(oldYLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)));
}
else
{
@@ -268,7 +282,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
{
oldZLocal = Local();
- this.Copy(oldZLocal, Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask));
+ this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
}
else
{
@@ -293,17 +307,30 @@ namespace Ryujinx.Graphics.Shader.Translation
}
else if (Config.Stage == ShaderStage.Geometry)
{
- void WriteOutput(int index, int primIndex)
+ void WritePositionOutput(int primIndex)
+ {
+ Operand x = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(0));
+ Operand y = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(1));
+ Operand z = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(2));
+ Operand w = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(3));
+
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), x);
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), y);
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), z);
+ this.Store(StorageKind.Output, IoVariable.Position, null, Const(3), w);
+ }
+
+ void WriteUserDefinedOutput(int index, int primIndex)
{
- Operand x = this.LoadAttribute(Const(index), Const(0), Const(primIndex));
- Operand y = this.LoadAttribute(Const(index + 4), Const(0), Const(primIndex));
- Operand z = this.LoadAttribute(Const(index + 8), Const(0), Const(primIndex));
- Operand w = this.LoadAttribute(Const(index + 12), Const(0), Const(primIndex));
-
- this.Copy(Attribute(index), x);
- this.Copy(Attribute(index + 4), y);
- this.Copy(Attribute(index + 8), z);
- this.Copy(Attribute(index + 12), w);
+ Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(0));
+ Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(1));
+ Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(2));
+ Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(3));
+
+ this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x);
+ this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y);
+ this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(2), z);
+ this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w);
}
if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
@@ -312,13 +339,13 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int primIndex = 0; primIndex < inputVertices; primIndex++)
{
- WriteOutput(AttributeConsts.PositionX, primIndex);
+ WritePositionOutput(primIndex);
int passthroughAttributes = Config.PassthroughAttributes;
while (passthroughAttributes != 0)
{
int index = BitOperations.TrailingZeroCount(passthroughAttributes);
- WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
+ WriteUserDefinedOutput(index, primIndex);
Config.SetOutputUserAttribute(index);
passthroughAttributes &= ~(1 << index);
}
@@ -337,11 +364,9 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Config.OmapDepth)
{
- Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
-
Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr);
- this.Copy(dest, src);
+ this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src);
}
AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
@@ -390,32 +415,30 @@ namespace Ryujinx.Graphics.Shader.Translation
continue;
}
- int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16;
-
Operand src = Register(regIndexBase + component, RegisterType.Gpr);
// Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
if (!supportsBgra && (component == 0 || component == 2))
{
- Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4);
+ Operand isBgra = this.Load(StorageKind.Input, IoVariable.FragmentOutputIsBgra, null, Const(rtIndex));
Operand lblIsBgra = Label();
Operand lblEnd = Label();
this.BranchIfTrue(lblIsBgra, isBgra);
- this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
+ this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
this.Branch(lblEnd);
MarkLabel(lblIsBgra);
- this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src);
+ this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(2 - component), src);
MarkLabel(lblEnd);
}
else
{
- this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src);
+ this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src);
}
}
@@ -441,8 +464,11 @@ namespace Ryujinx.Graphics.Shader.Translation
// 11 01 01 01 01 00 00 00
Operand ditherMask = Const(unchecked((int)0xfbb99110u));
- Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1));
- Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1));
+ Operand fragCoordX = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0));
+ Operand fragCoordY = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1));
+
+ Operand x = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordX), Const(1));
+ Operand y = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordY), Const(1));
Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
Operand alpha = Register(3, RegisterType.Gpr);
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
index 1fb60508..93748249 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
@@ -7,54 +7,54 @@ namespace Ryujinx.Graphics.Shader.Translation
{
static class EmitterContextInsts
{
- public static Operand AtomicAdd(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicAdd(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicAdd | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicAdd, storageKind, Local(), a, b, c);
}
- public static Operand AtomicAnd(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicAnd(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicAnd | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicAnd, storageKind, Local(), a, b, c);
}
- public static Operand AtomicCompareAndSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c, Operand d)
+ public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c, Operand d)
{
- return context.Add(Instruction.AtomicCompareAndSwap | mr, Local(), a, b, c, d);
+ return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), a, b, c, d);
}
- public static Operand AtomicMaxS32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicMaxS32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicMaxS32 | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicMaxS32, storageKind, Local(), a, b, c);
}
- public static Operand AtomicMaxU32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicMaxU32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicMaxU32 | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicMaxU32, storageKind, Local(), a, b, c);
}
- public static Operand AtomicMinS32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicMinS32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicMinS32 | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicMinS32, storageKind, Local(), a, b, c);
}
- public static Operand AtomicMinU32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicMinU32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicMinU32 | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicMinU32, storageKind, Local(), a, b, c);
}
- public static Operand AtomicOr(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicOr(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicOr | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicOr, storageKind, Local(), a, b, c);
}
- public static Operand AtomicSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicSwap(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicSwap | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicSwap, storageKind, Local(), a, b, c);
}
- public static Operand AtomicXor(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c)
+ public static Operand AtomicXor(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.AtomicXor | mr, Local(), a, b, c);
+ return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c);
}
public static Operand Ballot(this EmitterContext context, Operand a)
@@ -549,9 +549,36 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(fpType | Instruction.IsNan, Local(), a);
}
- public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b, Operand c)
+ public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null)
{
- return context.Add(Instruction.LoadAttribute, Local(), a, b, c);
+ return primVertex != null
+ ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex)
+ : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable));
+ }
+
+ public static Operand Load(
+ this EmitterContext context,
+ StorageKind storageKind,
+ IoVariable ioVariable,
+ Operand primVertex,
+ Operand elemIndex)
+ {
+ return primVertex != null
+ ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex)
+ : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex);
+ }
+
+ public static Operand Load(
+ this EmitterContext context,
+ StorageKind storageKind,
+ IoVariable ioVariable,
+ Operand primVertex,
+ Operand arrayIndex,
+ Operand elemIndex)
+ {
+ return primVertex != null
+ ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex)
+ : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex);
}
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
@@ -662,9 +689,43 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
}
- public static Operand StoreAttribute(this EmitterContext context, Operand a, Operand b, Operand c)
+ public static Operand Store(
+ this EmitterContext context,
+ StorageKind storageKind,
+ IoVariable ioVariable,
+ Operand invocationId,
+ Operand value)
+ {
+ return invocationId != null
+ ? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, value)
+ : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), value);
+ }
+
+ public static Operand Store(
+ this EmitterContext context,
+ StorageKind storageKind,
+ IoVariable ioVariable,
+ Operand invocationId,
+ Operand elemIndex,
+ Operand value)
+ {
+ return invocationId != null
+ ? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, elemIndex, value)
+ : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), elemIndex, value);
+ }
+
+ public static Operand Store(
+ this EmitterContext context,
+ StorageKind storageKind,
+ IoVariable ioVariable,
+ Operand invocationId,
+ Operand arrayIndex,
+ Operand elemIndex,
+ Operand value)
{
- return context.Add(Instruction.StoreAttribute, null, a, b, c);
+ return invocationId != null
+ ? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, arrayIndex, elemIndex, value)
+ : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
}
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
diff --git a/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs b/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs
index 3915c0d5..774a128d 100644
--- a/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs
+++ b/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs
@@ -16,20 +16,15 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int UbeDescsSize = StorageDescSize * UbeMaxCount;
public const int UbeFirstCbuf = 8;
- public static bool UsesGlobalMemory(Instruction inst)
+ public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind)
{
- return (inst.IsAtomic() && IsGlobalMr(inst)) ||
+ return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) ||
inst == Instruction.LoadGlobal ||
inst == Instruction.StoreGlobal ||
inst == Instruction.StoreGlobal16 ||
inst == Instruction.StoreGlobal8;
}
- private static bool IsGlobalMr(Instruction inst)
- {
- return (inst & Instruction.MrMask) == Instruction.MrGlobal;
- }
-
public static int GetStorageCbOffset(ShaderStage stage, int slot)
{
return GetStorageBaseCbOffset(stage) + slot * StorageDescSize;
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
index c280a6d8..2a4070e0 100644
--- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
- if (UsesGlobalMemory(operation.Inst))
+ if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
{
Operand source = operation.GetSource(0);
@@ -104,9 +104,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (isAtomic)
{
- Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
-
- storageOp = new Operation(inst, operation.Dest, sources);
+ storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources);
}
else if (operation.Inst == Instruction.LoadGlobal)
{
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
index a2219b36..bae774ee 100644
--- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
@@ -170,10 +170,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return false;
}
- return x.Type == OperandType.Attribute ||
- x.Type == OperandType.AttributePerPatch ||
- x.Type == OperandType.Constant ||
- x.Type == OperandType.ConstantBuffer;
+ // TODO: Handle Load operations with the same storage and the same constant parameters.
+ return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
}
private static bool PropagatePack(Operation packOp)
diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
index 3ec4e49a..91e7ace1 100644
--- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
if (hasConstantBufferDrawParameters)
{
- if (ReplaceConstantBufferWithDrawParameters(operation))
+ if (ReplaceConstantBufferWithDrawParameters(node, operation))
{
config.SetUsedFeature(FeatureFlags.DrawParameters);
}
@@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation
nextNode = node.Next;
}
- else if (UsesGlobalMemory(operation.Inst))
+ else if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
{
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
}
@@ -169,9 +169,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (isAtomic)
{
- Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
-
- storageOp = new Operation(inst, operation.Dest, sources);
+ storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources);
}
else if (operation.Inst == Instruction.LoadGlobal)
{
@@ -708,8 +706,15 @@ namespace Ryujinx.Graphics.Shader.Translation
return node;
}
- private static bool ReplaceConstantBufferWithDrawParameters(Operation operation)
+ private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation)
{
+ Operand GenerateLoad(IoVariable ioVariable)
+ {
+ Operand value = Local();
+ node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, value, Const((int)ioVariable)));
+ return value;
+ }
+
bool modified = false;
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
@@ -721,15 +726,15 @@ namespace Ryujinx.Graphics.Shader.Translation
switch (src.GetCbufOffset())
{
case Constants.NvnBaseVertexByteOffset / 4:
- operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex));
+ operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseVertex));
modified = true;
break;
case Constants.NvnBaseInstanceByteOffset / 4:
- operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance));
+ operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseInstance));
modified = true;
break;
case Constants.NvnDrawIndexByteOffset / 4:
- operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex));
+ operation.SetSource(srcIndex, GenerateLoad(IoVariable.DrawIndex));
modified = true;
break;
}
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index 15eb7ed1..22f5a671 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -41,6 +41,46 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool TransformFeedbackEnabled { get; }
+ private TransformFeedbackOutput[] _transformFeedbackOutputs;
+
+ readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable>
+ {
+ public IoVariable IoVariable { get; }
+ public int Location { get; }
+ public int Component { get; }
+
+ public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0)
+ {
+ IoVariable = ioVariable;
+ Location = location;
+ Component = component;
+ }
+
+ public override bool Equals(object other)
+ {
+ return other is TransformFeedbackVariable tfbVar && Equals(tfbVar);
+ }
+
+ public bool Equals(TransformFeedbackVariable other)
+ {
+ return IoVariable == other.IoVariable &&
+ Location == other.Location &&
+ Component == other.Component;
+ }
+
+ public override int GetHashCode()
+ {
+ return (int)IoVariable | (Location << 8) | (Component << 16);
+ }
+
+ public override string ToString()
+ {
+ return $"{IoVariable}.{Location}.{Component}";
+ }
+ }
+
+ private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions;
+
public int Size { get; private set; }
public byte ClipDistancesWritten { get; private set; }
@@ -102,6 +142,8 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor = gpuAccessor;
Options = options;
+ _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
+
AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1;
AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1;
@@ -147,6 +189,173 @@ namespace Ryujinx.Graphics.Shader.Translation
LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
}
+ private void EnsureTransformFeedbackInitialized()
+ {
+ if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null)
+ {
+ TransformFeedbackOutput[] transformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
+ ulong vecMap = 0UL;
+
+ for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
+ {
+ var locations = GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
+ var stride = GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
+
+ for (int i = 0; i < locations.Length; i++)
+ {
+ byte wordOffset = locations[i];
+ if (wordOffset < 0xc0)
+ {
+ transformFeedbackOutputs[wordOffset] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
+ vecMap |= 1UL << (wordOffset / 4);
+ }
+ }
+ }
+
+ _transformFeedbackOutputs = transformFeedbackOutputs;
+
+ while (vecMap != 0)
+ {
+ int vecIndex = BitOperations.TrailingZeroCount(vecMap);
+
+ for (int subIndex = 0; subIndex < 4; subIndex++)
+ {
+ int wordOffset = vecIndex * 4 + subIndex;
+ int byteOffset = wordOffset * 4;
+
+ if (transformFeedbackOutputs[wordOffset].Valid)
+ {
+ IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location);
+ int component = 0;
+
+ if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true))
+ {
+ component = subIndex;
+ }
+
+ var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
+ _transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]);
+ }
+ }
+
+ vecMap &= ~(1UL << vecIndex);
+ }
+ }
+ }
+
+ public TransformFeedbackOutput[] GetTransformFeedbackOutputs()
+ {
+ EnsureTransformFeedbackInitialized();
+ return _transformFeedbackOutputs;
+ }
+
+ public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput)
+ {
+ EnsureTransformFeedbackInitialized();
+ var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component);
+ return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput);
+ }
+
+ private bool HasTransformFeedbackOutputs()
+ {
+ return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment);
+ }
+
+ public bool HasTransformFeedbackOutputs(bool isOutput)
+ {
+ return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment));
+ }
+
+ public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput)
+ {
+ if (ioVariable == IoVariable.UserDefined)
+ {
+ return (!isOutput && !UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
+ (isOutput && !UsedFeatures.HasFlag(FeatureFlags.OaIndexing));
+ }
+
+ return ioVariable == IoVariable.FragmentOutputColor;
+ }
+
+ public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput)
+ {
+ if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput))
+ {
+ return false;
+ }
+
+ return GetTransformFeedbackOutputComponents(location, component) == 1;
+ }
+
+ public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset)
+ {
+ EnsureTransformFeedbackInitialized();
+
+ return _transformFeedbackOutputs[wordOffset];
+ }
+
+ public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
+ {
+ return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component);
+ }
+
+ public int GetTransformFeedbackOutputComponents(int location, int component)
+ {
+ EnsureTransformFeedbackInitialized();
+
+ int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4;
+ int index = baseIndex + component;
+ int count = 1;
+
+ for (; count < 4; count++)
+ {
+ ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1];
+ ref var curr = ref _transformFeedbackOutputs[baseIndex + count];
+
+ int prevOffset = prev.Offset;
+ int currOffset = curr.Offset;
+
+ if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
+ {
+ break;
+ }
+ }
+
+ if (baseIndex + count <= index)
+ {
+ return 1;
+ }
+
+ return count;
+ }
+
+ public AggregateType GetFragmentOutputColorType(int location)
+ {
+ return AggregateType.Vector4 | GpuAccessor.QueryFragmentOutputType(location).ToAggregateType();
+ }
+
+ public AggregateType GetUserDefinedType(int location, bool isOutput)
+ {
+ if ((!isOutput && UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) ||
+ (isOutput && UsedFeatures.HasFlag(FeatureFlags.OaIndexing)))
+ {
+ return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32;
+ }
+
+ AggregateType type = AggregateType.Vector4;
+
+ if (Stage == ShaderStage.Vertex && !isOutput)
+ {
+ type |= GpuAccessor.QueryAttributeType(location).ToAggregateType();
+ }
+ else
+ {
+ type |= AggregateType.FP32;
+ }
+
+ return type;
+ }
+
public int GetDepthRegister()
{
// The depth register is always two registers after the last color output.
@@ -184,7 +393,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return format;
}
- private bool FormatSupportsAtomic(TextureFormat format)
+ private static bool FormatSupportsAtomic(TextureFormat format)
{
return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint;
}
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs
index 206718f2..53f1e847 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs
@@ -53,40 +53,80 @@ namespace Ryujinx.Graphics.Shader.Translation
return false;
}
- if (operation.Inst == Instruction.StoreAttribute)
+ if (operation.Inst == Instruction.Store && operation.StorageKind == StorageKind.Output)
{
- return false;
- }
-
- if (operation.Inst == Instruction.Copy && operation.Dest.Type == OperandType.Attribute)
- {
- Operand src = operation.GetSource(0);
+ Operand src = operation.GetSource(operation.SourcesCount - 1);
+ Operation srcAttributeAsgOp = null;
- if (src.Type == OperandType.LocalVariable && src.AsgOp is Operation asgOp && asgOp.Inst == Instruction.LoadAttribute)
+ if (src.Type == OperandType.LocalVariable &&
+ src.AsgOp is Operation asgOp &&
+ asgOp.Inst == Instruction.Load &&
+ asgOp.StorageKind.IsInputOrOutput())
{
- src = Attribute(asgOp.GetSource(0).Value);
+ if (asgOp.StorageKind != StorageKind.Input)
+ {
+ return false;
+ }
+
+ srcAttributeAsgOp = asgOp;
}
- if (src.Type == OperandType.Attribute)
+ if (srcAttributeAsgOp != null)
{
- if (operation.Dest.Value == AttributeConsts.Layer)
+ IoVariable dstAttribute = (IoVariable)operation.GetSource(0).Value;
+ IoVariable srcAttribute = (IoVariable)srcAttributeAsgOp.GetSource(0).Value;
+
+ if (dstAttribute == IoVariable.Layer && srcAttribute == IoVariable.UserDefined)
{
- if ((src.Value & AttributeConsts.LoadOutputMask) != 0)
+ if (srcAttributeAsgOp.SourcesCount != 4)
{
return false;
}
writesLayer = true;
- layerInputAttr = src.Value;
+ layerInputAttr = srcAttributeAsgOp.GetSource(1).Value * 4 + srcAttributeAsgOp.GetSource(3).Value;;
}
- else if (src.Value != operation.Dest.Value)
+ else
{
- return false;
+ if (dstAttribute != srcAttribute)
+ {
+ return false;
+ }
+
+ int inputsCount = operation.SourcesCount - 2;
+
+ if (dstAttribute == IoVariable.UserDefined)
+ {
+ if (operation.GetSource(1).Value != srcAttributeAsgOp.GetSource(1).Value)
+ {
+ return false;
+ }
+
+ inputsCount--;
+ }
+
+ for (int i = 0; i < inputsCount; i++)
+ {
+ int dstIndex = operation.SourcesCount - 2 - i;
+ int srcIndex = srcAttributeAsgOp.SourcesCount - 1 - i;
+
+ if ((dstIndex | srcIndex) < 0)
+ {
+ return false;
+ }
+
+ if (operation.GetSource(dstIndex).Type != OperandType.Constant ||
+ srcAttributeAsgOp.GetSource(srcIndex).Type != OperandType.Constant ||
+ operation.GetSource(dstIndex).Value != srcAttributeAsgOp.GetSource(srcIndex).Value)
+ {
+ return false;
+ }
+ }
}
}
else if (src.Type == OperandType.Constant)
{
- int dstComponent = (operation.Dest.Value >> 2) & 3;
+ int dstComponent = operation.GetSource(operation.SourcesCount - 2).Value;
float expectedValue = dstComponent == 3 ? 1f : 0f;
if (src.AsFloat() != expectedValue)
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index 6a123045..77d3b568 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (config.Stage == ShaderStage.Vertex)
{
- InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
+ InitializePositionOutput(context);
}
UInt128 usedAttributes = context.Config.NextInputAttributesComponents;
@@ -194,20 +194,23 @@ namespace Ryujinx.Graphics.Shader.Translation
continue;
}
- InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
+ InitializeOutputComponent(context, vecIndex, index & 3, perPatch: false);
}
if (context.Config.NextUsedInputAttributesPerPatch != null)
{
foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.Order())
{
- InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true);
+ InitializeOutput(context, vecIndex, perPatch: true);
}
}
if (config.NextUsesFixedFuncAttributes)
{
- for (int i = 0; i < 4 + AttributeConsts.TexCoordCount; i++)
+ bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
+ int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1;
+
+ for (int i = fixedStartAttr; i < fixedStartAttr + 5 + AttributeConsts.TexCoordCount; i++)
{
int index = config.GetFreeUserAttribute(isOutput: true, i);
if (index < 0)
@@ -215,26 +218,58 @@ namespace Ryujinx.Graphics.Shader.Translation
break;
}
- InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
+ InitializeOutput(context, index, perPatch: false);
config.SetOutputUserAttributeFixedFunc(index);
}
}
}
- private static void InitializeOutput(EmitterContext context, int baseAttr, bool perPatch)
+ private static void InitializePositionOutput(EmitterContext context)
{
for (int c = 0; c < 4; c++)
{
- int attrOffset = baseAttr + c * 4;
- InitializeOutputComponent(context, attrOffset, perPatch);
+ context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
}
}
- private static void InitializeOutputComponent(EmitterContext context, int attrOffset, bool perPatch)
+ private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
{
- int c = (attrOffset >> 2) & 3;
- context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f));
+ for (int c = 0; c < 4; c++)
+ {
+ InitializeOutputComponent(context, location, c, perPatch);
+ }
+ }
+
+ private static void InitializeOutputComponent(EmitterContext context, int location, int c, bool perPatch)
+ {
+ StorageKind storageKind = perPatch ? StorageKind.OutputPerPatch : StorageKind.Output;
+
+ if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))
+ {
+ Operand invocationId = null;
+
+ if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch)
+ {
+ invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
+ }
+
+ int index = location * 4 + c;
+
+ context.Store(storageKind, IoVariable.UserDefined, invocationId, Const(index), ConstF(c == 3 ? 1f : 0f));
+ }
+ else
+ {
+ if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch)
+ {
+ Operand invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId);
+ context.Store(storageKind, IoVariable.UserDefined, Const(location), invocationId, Const(c), ConstF(c == 3 ? 1f : 0f));
+ }
+ else
+ {
+ context.Store(storageKind, IoVariable.UserDefined, null, Const(location), Const(c), ConstF(c == 3 ? 1f : 0f));
+ }
+ }
}
private static void EmitOps(EmitterContext context, Block block)
diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
index 856b16b7..4a304f3a 100644
--- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
@@ -34,15 +34,16 @@ namespace Ryujinx.Graphics.Shader.Translation
_config = config;
}
- private static bool IsUserAttribute(Operand operand)
+ private static bool IsLoadUserDefined(Operation operation)
{
- if (operand != null && operand.Type.IsAttribute())
- {
- int value = operand.Value & AttributeConsts.Mask;
- return value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd;
- }
+ // TODO: Check if sources count match and all sources are constant.
+ return operation.Inst == Instruction.Load && (IoVariable)operation.GetSource(0).Value == IoVariable.UserDefined;
+ }
- return false;
+ private static bool IsStoreUserDefined(Operation operation)
+ {
+ // TODO: Check if sources count match and all sources are constant.
+ return operation.Inst == Instruction.Store && (IoVariable)operation.GetSource(0).Value == IoVariable.UserDefined;
}
private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart)
@@ -68,9 +69,9 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operation operation = a[0].Code[index];
- if (IsUserAttribute(operation.Dest))
+ if (IsStoreUserDefined(operation))
{
- int tIndex = (operation.Dest.Value - AttributeConsts.UserAttributeBase) / 4;
+ int tIndex = operation.GetSource(1).Value * 4 + operation.GetSource(2).Value;
Operand temp = temps[tIndex];
@@ -82,6 +83,7 @@ namespace Ryujinx.Graphics.Shader.Translation
}
operation.Dest = temp;
+ operation.TurnIntoCopy(operation.GetSource(operation.SourcesCount - 1));
}
if (operation.Inst == Instruction.Return)
@@ -100,18 +102,15 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operation operation = b[0].Code[index];
- for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
+ if (IsLoadUserDefined(operation))
{
- Operand src = operation.GetSource(srcIndex);
+ int tIndex = operation.GetSource(1).Value * 4 + operation.GetSource(2).Value;
- if (IsUserAttribute(src))
- {
- Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4];
+ Operand temp = temps[tIndex];
- if (temp != null)
- {
- operation.SetSource(srcIndex, temp);
- }
+ if (temp != null)
+ {
+ operation.TurnIntoCopy(temp);
}
}
@@ -209,15 +208,15 @@ namespace Ryujinx.Graphics.Shader.Translation
{
int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4;
- Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
+ Operand value = context.Load(StorageKind.Input, IoVariable.UserDefined, Const(v), Const(attrIndex), Const(c));
if (attr == layerOutputAttr)
{
- context.Copy(Attribute(AttributeConsts.Layer), value);
+ context.Store(StorageKind.Output, IoVariable.Layer, null, value);
}
else
{
- context.Copy(Attribute(attr), value);
+ context.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(attrIndex), Const(c), value);
config.SetOutputUserAttribute(attrIndex);
}
@@ -227,11 +226,9 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int c = 0; c < 4; c++)
{
- int attr = AttributeConsts.PositionX + c * 4;
-
- Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
+ Operand value = context.Load(StorageKind.Input, IoVariable.Position, Const(v), Const(c));
- context.Copy(Attribute(attr), value);
+ context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), value);
}
context.EmitVertex();
diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
index e206bb29..ab82d7b4 100644
--- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
+++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
@@ -40,6 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsPreciseOcclusionQueries;
public readonly bool SupportsPipelineStatisticsQuery;
public readonly bool SupportsGeometryShader;
+ public readonly bool SupportsViewportArray2;
public readonly uint MinSubgroupSize;
public readonly uint MaxSubgroupSize;
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
@@ -73,6 +74,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsPreciseOcclusionQueries,
bool supportsPipelineStatisticsQuery,
bool supportsGeometryShader,
+ bool supportsViewportArray2,
uint minSubgroupSize,
uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages,
@@ -105,6 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;
SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery;
SupportsGeometryShader = supportsGeometryShader;
+ SupportsViewportArray2 = supportsViewportArray2;
MinSubgroupSize = minSubgroupSize;
MaxSubgroupSize = maxSubgroupSize;
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
diff --git a/Ryujinx.Graphics.Vulkan/Shader.cs b/Ryujinx.Graphics.Vulkan/Shader.cs
index 26d0ca40..ca99ebf0 100644
--- a/Ryujinx.Graphics.Vulkan/Shader.cs
+++ b/Ryujinx.Graphics.Vulkan/Shader.cs
@@ -76,10 +76,6 @@ namespace Ryujinx.Graphics.Vulkan
private unsafe static byte[] GlslToSpirv(string glsl, ShaderStage stage)
{
- // TODO: We should generate the correct code on the shader translator instead of doing this compensation.
- glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)");
- glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)");
-
Options options;
lock (_shaderOptionsLock)
diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index 4f69cb1d..50a6fcb9 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -39,7 +39,8 @@ namespace Ryujinx.Graphics.Vulkan
"VK_EXT_shader_subgroup_ballot",
"VK_EXT_subgroup_size_control",
"VK_NV_geometry_shader_passthrough",
- "VK_KHR_portability_subset", // By spec, we should enable this if present.
+ "VK_NV_viewport_array2",
+ "VK_KHR_portability_subset" // As per spec, we should enable this if present.
};
private static readonly string[] _requiredExtensions = new string[]
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 1c295d6f..e7475b6b 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -306,6 +306,7 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.OcclusionQueryPrecise,
_physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
_physicalDevice.PhysicalDeviceFeatures.GeometryShader,
+ _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
@@ -568,7 +569,8 @@ namespace Ryujinx.Graphics.Vulkan
supportsNonConstantTextureOffset: false,
supportsShaderBallot: false,
supportsTextureShadowLod: false,
- supportsViewportIndex: featuresVk12.ShaderOutputViewportIndex,
+ supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex,
+ supportsViewportMask: Capabilities.SupportsViewportArray2,
supportsViewportSwizzle: false,
supportsIndirectParameters: true,
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs
index 746b780c..3acebbda 100644
--- a/Ryujinx.ShaderTools/Program.cs
+++ b/Ryujinx.ShaderTools/Program.cs
@@ -86,8 +86,8 @@ namespace Ryujinx.ShaderTools
static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
- .WithParsed(options => HandleArguments(options))
- .WithNotParsed(errors => errors.Output());
+ .WithParsed(options => HandleArguments(options))
+ .WithNotParsed(errors => errors.Output());
}
}
} \ No newline at end of file