From 9f12e50a546b15533778ed0d8290202af91c10a2 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 25 Apr 2023 19:51:07 -0300 Subject: [PATCH] Refactor attribute handling on the shader generator (#4565) * Refactor attribute handling on the shader generator * Implement gl_ViewportMask[] * Add back the Intel FrontFacing bug workaround * Fix GLSL transform feedback outputs mistmatch with fragment stage * Shader cache version bump * Fix geometry shader recognition * PR feedback * Delete GetOperandDef and GetOperandUse * Remove replacements that are no longer needed on GLSL compilation on Vulkan * Fix incorrect load for per-patch outputs * Fix build --- Ryujinx.Graphics.GAL/Capabilities.cs | 9 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 4 +- Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 2 + Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 3 +- .../CodeGen/Glsl/Declarations.cs | 35 +- .../CodeGen/Glsl/GlslGenerator.cs | 16 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 20 +- .../Glsl/Instructions/InstGenHelper.cs | 4 +- .../Glsl/Instructions/InstGenMemory.cs | 158 ++++--- .../CodeGen/Glsl/Instructions/IoMap.cs | 145 +++++++ .../CodeGen/Glsl/OperandManager.cs | 345 +++------------- .../CodeGen/Spirv/CodeGenContext.cs | 255 +----------- .../CodeGen/Spirv/Declarations.cs | 386 +++++------------- .../CodeGen/Spirv/EnumConversion.cs | 3 +- .../CodeGen/Spirv/Instructions.cs | 241 ++++++++--- .../CodeGen/Spirv/IoMap.cs | 86 ++++ .../CodeGen/Spirv/ScalingHelpers.cs | 2 +- .../CodeGen/Spirv/SpirvGenerator.cs | 31 +- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 5 +- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 19 +- .../Instructions/AttributeMap.cs | 351 ++++++++++++++++ .../Instructions/InstEmitAttribute.cs | 148 ++++--- .../Instructions/InstEmitMemory.cs | 24 +- .../Instructions/InstEmitMove.cs | 36 +- .../IntermediateRepresentation/Instruction.cs | 11 +- .../IntermediateRepresentation/IoVariable.cs | 51 +++ .../OperandHelper.cs | 10 - .../IntermediateRepresentation/OperandType.cs | 10 - .../IntermediateRepresentation/Operation.cs | 18 + .../IntermediateRepresentation/StorageKind.cs | 39 ++ .../StructuredIr/AstOperation.cs | 10 +- .../StructuredIr/AstTextureOperation.cs | 2 +- .../StructuredIr/InstructionInfo.cs | 6 +- .../StructuredIr/IoDefinition.cs | 44 ++ .../StructuredIr/OperandInfo.cs | 2 - .../StructuredIr/StructuredProgram.cs | 77 ++-- .../StructuredIr/StructuredProgramContext.cs | 67 +-- .../StructuredIr/StructuredProgramInfo.cs | 49 +-- .../Translation/AttributeConsts.cs | 121 ++---- .../Translation/AttributeInfo.cs | 210 ---------- .../Translation/EmitterContext.cs | 98 +++-- .../Translation/EmitterContextInsts.cs | 109 +++-- .../Translation/GlobalMemory.cs | 9 +- .../Optimizations/GlobalToStorage.cs | 6 +- .../Translation/Optimizations/Optimizer.cs | 6 +- .../Translation/Rewriter.cs | 23 +- .../Translation/ShaderConfig.cs | 211 +++++++++- .../Translation/ShaderIdentifier.cs | 72 +++- .../Translation/Translator.cs | 57 ++- .../Translation/TranslatorContext.cs | 47 +-- .../HardwareCapabilities.cs | 3 + Ryujinx.Graphics.Vulkan/Shader.cs | 4 - .../VulkanInitialization.cs | 3 +- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 4 +- Ryujinx.ShaderTools/Program.cs | 4 +- 56 files changed, 1967 insertions(+), 1746 deletions(-) create mode 100644 Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs create mode 100644 Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs create mode 100644 Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs create mode 100644 Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs create mode 100644 Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs create mode 100644 Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs delete mode 100644 Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index bc4a02c97..a93d38466 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 cad2341b2..78f9763fb 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 bbf2702e4..d35b8d921 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; /// /// 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 846465260..bf365b4de 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 _supportsSeamlessCubemapPerTexture = new Lazy(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); private static readonly Lazy _supportsShaderBallot = new Lazy(() => HasExtension("GL_ARB_shader_ballot")); private static readonly Lazy _supportsShaderViewportLayerArray = new Lazy(() => HasExtension("GL_ARB_shader_viewport_layer_array")); + private static readonly Lazy _supportsViewportArray2 = new Lazy(() => HasExtension("GL_NV_viewport_array2")); private static readonly Lazy _supportsTextureCompressionBptc = new Lazy(() => HasExtension("GL_EXT_texture_compression_bptc")); private static readonly Lazy _supportsTextureCompressionRgtc = new Lazy(() => HasExtension("GL_EXT_texture_compression_rgtc")); private static readonly Lazy _supportsTextureCompressionS3tc = new Lazy(() => 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 5a2e3fe4e..3903b4d4e 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 5e53d62a1..81b79ec45 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 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 907275583..751d03507 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 9ca4618d3..01bd11e59 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 743b695c8..00478f6ab 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 a5d2632ce..99519837b 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 000000000..093ee2322 --- /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 ec761fa66..92e833580 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 _builtInAttributes = new Dictionary() - { - { 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 _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."); + } - return OperandInfo.GetVarType(OperandType.Attribute); + 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; + } + } + + (_, 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 e693307da..ed292ef1a 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 SamplersTypes { get; } = new Dictionary(); public Dictionary Samplers { get; } = new Dictionary(); public Dictionary Images { get; } = new Dictionary(); - public Dictionary Inputs { get; } = new Dictionary(); - public Dictionary Outputs { get; } = new Dictionary(); - public Dictionary InputsPerPatch { get; } = new Dictionary(); - public Dictionary OutputsPerPatch { get; } = new Dictionary(); + public Dictionary Inputs { get; } = new Dictionary(); + public Dictionary Outputs { get; } = new Dictionary(); + public Dictionary InputsPerPatch { get; } = new Dictionary(); + public Dictionary OutputsPerPatch { get; } = new Dictionary(); public Instruction CoordTemp { get; set; } private readonly Dictionary _locals = new Dictionary(); @@ -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 fdca5e890..821da4773 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)); + var ioVariable = ioDefinition.IoVariable; - 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; - - 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); + IoVariable ioVariable = ioDefinition.IoVariable; + var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; - if (oaIndexing && !perPatch) + bool isBuiltIn; + BuiltIn builtIn = default; + AggregateType varType; + + if (ioVariable == IoVariable.UserDefined) { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); + varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput); + isBuiltIn = false; + } + else if (ioVariable == IoVariable.FragmentOutputColor) + { + varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location); + isBuiltIn = false; + } + else + { + (builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable); + isBuiltIn = true; - if (context.Config.Stage == ShaderStage.TessellationControl) + if (varType == AggregateType.Invalid) { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + throw new InvalidOperationException($"Unknown variable {ioVariable}."); } - - var spvType = context.TypePointer(StorageClass.Output, attrType); - var spvVar = context.Variable(spvType, StorageClass.Output); - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.OutputsArray = spvVar; } - var outputs = perPatch ? info.OutputsPerPatch : info.Outputs; + bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput); - foreach (int attr in outputs) + if (hasComponent) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch)) + varType &= AggregateType.ElementTypeMask; + } + else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput)) + { + varType &= AggregateType.ElementTypeMask; + varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch { - continue; - } - - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; - - if (oaIndexing && isUserAttr && !perPatch) - { - continue; - } - - DeclareOutputAttribute(context, attr, perPatch); + 2 => AggregateType.Vector2, + 3 => AggregateType.Vector3, + 4 => AggregateType.Vector4, + _ => AggregateType.Invalid + }; } - 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); - } - - 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))) - { - DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq); - return; - } - - 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)) - { - return; - } - - 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 aa3d046a2..72541774d 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 b3db19051..b6ffdb7ac 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 000000000..d2ff0085c --- /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 8503771c3..f6c218c67 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 ca8235383..3e11a9749 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 380c425e5..c619b9bbc 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 bc5e67c35..2207156cd 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -305,9 +305,9 @@ namespace Ryujinx.Graphics.Shader } /// - /// 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. /// - /// True if writes to layer from vertex or tessellation are supported, false otherwise + /// True if writes to the layer from vertex or tessellation are supported, false otherwise bool QueryHostSupportsLayerVertexTessellation() { return true; @@ -350,10 +350,19 @@ namespace Ryujinx.Graphics.Shader } /// - /// Queries host GPU shader viewport index output support. + /// Queries host support for writes to the viewport index from vertex or tessellation shader stages. /// - /// True if the GPU and driver supports shader viewport index output, false otherwise - bool QueryHostSupportsViewportIndex() + /// True if writes to the viewport index from vertex or tessellation are supported, false otherwise + bool QueryHostSupportsViewportIndexVertexTessellation() + { + return true; + } + + /// + /// Queries host GPU shader viewport mask output support. + /// + /// True if the GPU and driver supports shader viewport mask output, false otherwise + 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 000000000..562fb8d53 --- /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 _attributes; + private static readonly IReadOnlyDictionary _attributesPerPatch; + + static AttributeMap() + { + _attributes = CreateMap(); + _attributesPerPatch = CreatePerPatchMap(); + } + + private static IReadOnlyDictionary CreateMap() + { + var map = new Dictionary(); + + 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 CreatePerPatchMap() + { + var map = new Dictionary(); + + 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 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 9f9ac1412..1df387618 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(); - 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; - } + bool isOutput = op.O && CanLoadOutput(offset); - Operand src = Const(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)); + 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.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd)); - - context.Config.SetUsedFeature(FeatureFlags.OaIndexing); + 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 ceb76de16..c73c6b2ac 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 16b02f978..9992ac378 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 aa9776bcf..d7c4a961c 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 000000000..a2163d14f --- /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 7fed861e5..37c349e82 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 7566a03f3..4d2da734e 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 18e203a70..99179f151 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(); + } + } + 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 000000000..593574438 --- /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 193972565..2393fd8d8 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 957a956fc..a44f13cc0 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 0a9a9e511..8eccef237 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 000000000..21a1b3f08 --- /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 + { + 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 730468a43..38ed1584b 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 b8d38fa65..b4ca8ee58 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 ce57a5788..68bbdeb14 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 489a59105..c51041467 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 Functions { get; } - public HashSet Inputs { get; } - public HashSet Outputs { get; } - public HashSet InputsPerPatch { get; } - public HashSet OutputsPerPatch { get; } + public HashSet IoDefinitions { get; } public HelperFunctionsMask HelperFunctionsMask { get; set; } - public TransformFeedbackOutput[] TransformFeedbackOutputs { get; } - public StructuredProgramInfo() { Functions = new List(); - Inputs = new HashSet(); - Outputs = new HashSet(); - InputsPerPatch = new HashSet(); - OutputsPerPatch = new HashSet(); - - 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(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 08efbc9fd..683b0d8ac 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 b671429a8..000000000 --- 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 _builtInAttributes = new Dictionary() - { - { 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 _builtInAttributesPerPatch = new Dictionary() - { - { 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 8f33cceda..e81f64252 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.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)); + 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.Copy(Attribute(index), x); - this.Copy(Attribute(index + 4), y); - this.Copy(Attribute(index + 8), z); - this.Copy(Attribute(index + 12), w); + 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.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 1fb605089..937482495 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 context.Add(Instruction.StoreAttribute, null, a, b, c); + 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 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 3915c0d55..774a128d8 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 c280a6d80..2a4070e0a 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 a2219b36d..bae774ee4 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 3ec4e49a7..91e7ace1e 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 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 15eb7ed1f..22f5a671d 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 + { + 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 _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(); + 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 206718f2a..53f1e8475 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; - } + Operand src = operation.GetSource(operation.SourcesCount - 1); + Operation srcAttributeAsgOp = null; - if (operation.Inst == Instruction.Copy && operation.Dest.Type == OperandType.Attribute) - { - Operand src = operation.GetSource(0); - - 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 6a1230458..77d3b568e 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 856b16b7d..4a304f3a8 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[tIndex]; + + if (temp != null) { - Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4]; - - if (temp != null) - { - operation.SetSource(srcIndex, temp); - } + 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.Load(StorageKind.Input, IoVariable.Position, Const(v), Const(c)); - Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v)); - - 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 e206bb299..ab82d7b4a 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 26d0ca408..ca99ebf07 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 4f69cb1d2..50a6fcb90 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 1c295d6ff..e7475b6b3 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 746b780c0..3acebbda6 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(args) - .WithParsed(options => HandleArguments(options)) - .WithNotParsed(errors => errors.Output()); + .WithParsed(options => HandleArguments(options)) + .WithNotParsed(errors => errors.Output()); } } } \ No newline at end of file