From 9c2500de5ffa76d74e1761be9e6a1e50b36af7c5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 1 Oct 2022 02:35:52 -0300 Subject: [PATCH] Fix incorrect tessellation inputs/outputs (#3728) * Fix incorrect tessellation inputs/outputs * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 51 ++++----- .../CodeGen/Glsl/OperandManager.cs | 83 ++++++++------- .../CodeGen/Spirv/CodeGenContext.cs | 10 +- .../CodeGen/Spirv/Declarations.cs | 26 +++-- .../CodeGen/Spirv/Instructions.cs | 3 +- .../CodeGen/Spirv/SpirvGenerator.cs | 15 ++- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 24 ++++- .../Instructions/InstEmitAttribute.cs | 9 +- .../StructuredIr/StructuredProgram.cs | 10 +- .../Translation/AttributeConsts.cs | 3 + .../Translation/AttributeInfo.cs | 91 ++++++++++------ .../Translation/EmitterContext.cs | 4 +- .../Translation/ShaderConfig.cs | 100 ++++++++++++------ .../Translation/Translator.cs | 14 ++- .../Translation/TranslatorContext.cs | 3 +- 16 files changed, 284 insertions(+), 164 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 24b05b90..e5efa0c5 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 = 3697; + private const uint CodeGenVersion = 3728; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 65e78121..ff808b04 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; +using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -163,9 +164,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (context.Config.Stage == ShaderStage.TessellationEvaluation) { + bool tessCw = context.Config.GpuAccessor.QueryTessCw(); + + if (context.Config.Options.TargetApi == TargetApi.Vulkan) + { + // We invert the front face on Vulkan backend, so we need to do that here aswell. + tessCw = !tessCw; + } + string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl(); string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl(); - string windingOrder = context.Config.GpuAccessor.QueryTessCw() ? "cw" : "ccw"; + string windingOrder = tessCw ? "cw" : "ccw"; context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); context.AppendLine(); @@ -185,14 +194,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if (context.Config.UsedInputAttributesPerPatch != 0) + if (context.Config.UsedInputAttributesPerPatch.Count != 0) { DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch); context.AppendLine(); } - if (context.Config.UsedOutputAttributesPerPatch != 0) + if (context.Config.UsedOutputAttributesPerPatch.Count != 0) { DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch); @@ -509,13 +518,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareInputAttributesPerPatch(CodeGenContext context, int usedAttributes) + private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet attrs) { - while (usedAttributes != 0) + foreach (int attr in attrs.OrderBy(x => x)) { - int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareInputAttributePerPatch(context, index); - usedAttributes &= ~(1 << index); + DeclareInputAttributePerPatch(context, attr); } } @@ -566,16 +573,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr) { - string layout = string.Empty; - - if (context.Config.Options.TargetApi == TargetApi.Vulkan) - { - layout = $"layout (location = {32 + attr}) "; - } - + int location = context.Config.GetPerPatchAttributeLocation(attr); string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; - context.AppendLine($"{layout}patch in vec4 {name};"); + context.AppendLine($"layout (location = {location}) patch in vec4 {name};"); } private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) @@ -624,28 +625,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, int usedAttributes) + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet attrs) { - while (usedAttributes != 0) + foreach (int attr in attrs.OrderBy(x => x)) { - int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareOutputAttributePerPatch(context, index); - usedAttributes &= ~(1 << index); + DeclareOutputAttributePerPatch(context, attr); } } private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr) { - string layout = string.Empty; - - if (context.Config.Options.TargetApi == TargetApi.Vulkan) - { - layout = $"layout (location = {32 + attr}) "; - } - + int location = context.Config.GetPerPatchAttributeLocation(attr); string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; - context.AppendLine($"{layout}patch out vec4 {name};"); + context.AppendLine($"layout (location = {location}) patch out vec4 {name};"); } private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 1ab91f77..fd284316 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -28,33 +28,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static Dictionary _builtInAttributes = new Dictionary() { - { AttributeConsts.TessLevelOuter0, new BuiltInAttribute("gl_TessLevelOuter[0]", VariableType.F32) }, - { AttributeConsts.TessLevelOuter1, new BuiltInAttribute("gl_TessLevelOuter[1]", VariableType.F32) }, - { AttributeConsts.TessLevelOuter2, new BuiltInAttribute("gl_TessLevelOuter[2]", VariableType.F32) }, - { AttributeConsts.TessLevelOuter3, new BuiltInAttribute("gl_TessLevelOuter[3]", VariableType.F32) }, - { AttributeConsts.TessLevelInner0, new BuiltInAttribute("gl_TessLevelInner[0]", VariableType.F32) }, - { AttributeConsts.TessLevelInner1, new BuiltInAttribute("gl_TessLevelInner[1]", VariableType.F32) }, - { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, - { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, - { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, - { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, - { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, - { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, - { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) }, - { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) }, - { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) }, - { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) }, - { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) }, - { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) }, - { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) }, - { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) }, - { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, - { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, - { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, - { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, - { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, - { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, - { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, + { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, + { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, + { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, + { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, + { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, + { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, + { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) }, + { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) }, + { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) }, + { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) }, + { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) }, + { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) }, + { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) }, + { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) }, + { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, + { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, + { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, + { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, + { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, + { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, + { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, // Special. { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, @@ -170,7 +164,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl value &= AttributeConsts.Mask & ~3; char swzMask = GetSwizzleMask((value >> 2) & 3); - if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) + 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) { value -= AttributeConsts.UserAttributeBase; @@ -180,11 +196,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing); - if (!indexable && perPatch) - { - prefix = DefaultNames.PerPatchAttributePrefix; - } - if (indexable) { string name = prefix; @@ -202,7 +213,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = $"{prefix}{(value >> 4)}_{swzMask}"; - if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) + if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) { name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } @@ -213,7 +224,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = $"{prefix}{(value >> 4)}"; - if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) + if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) { name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } @@ -277,7 +288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string name = builtInAttr.Name; - if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) + if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) { name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index d70a00ed..fe5e11f4 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -382,17 +382,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType) { var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrInfo = AttributeInfo.From(Config, attr, isOutAttr); + var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr); int attrOffset = attrInfo.BaseValue; - Instruction ioVariable; - - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; + Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset]; elemType = attrInfo.Type & AggregateType.ElementTypeMask; - ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset]; - if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0) { return ioVariable; @@ -404,7 +400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr) { - if (!AttributeInfo.Validate(Config, attr, isOutAttr: false)) + if (!AttributeInfo.ValidatePerPatch(Config, attr, isOutAttr: false)) { return GetConstant(type, new AstOperand(IrOperandType.Constant, 0)); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index dce5e48a..1a4decf5 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv foreach (int attr in inputs) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false)) + if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch)) { continue; } @@ -459,7 +459,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv foreach (int attr in outputs) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true)) + if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch)) { continue; } @@ -519,7 +519,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch) : (isOutAttr ? context.Outputs : context.Inputs); - var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); + var attrInfo = perPatch + ? AttributeInfo.FromPatch(context.Config, attr, isOutAttr) + : AttributeInfo.From(context.Config, attr, isOutAttr); if (dict.ContainsKey(attrInfo.BaseValue)) { @@ -544,11 +546,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var spvType = context.TypePointer(storageClass, attrType); var spvVar = context.Variable(spvType, storageClass); - if (perPatch) - { - context.Decorate(spvVar, Decoration.Patch); - } - if (builtInPassthrough) { context.Decorate(spvVar, Decoration.PassthroughNV); @@ -556,6 +553,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (attrInfo.IsBuiltin) { + if (perPatch) + { + context.Decorate(spvVar, Decoration.Patch); + } + context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) @@ -569,6 +571,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } } + else if (perPatch) + { + context.Decorate(spvVar, Decoration.Patch); + + int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16); + + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } else if (isUserAttr) { int location = (attr - AttributeConsts.UserAttributeBase) / 16; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index a7fb78b4..c743a274 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -882,7 +882,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (src2 is AstOperand operand && operand.Type == OperandType.Constant) { int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2); - return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr: false, index)); + bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0; + return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index)); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 23c6af81..fad7f9b8 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -191,7 +191,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } - if (context.Config.GpuAccessor.QueryTessCw()) + bool tessCw = context.Config.GpuAccessor.QueryTessCw(); + + if (context.Config.Options.TargetApi == TargetApi.Vulkan) + { + // We invert the front face on Vulkan backend, so we need to do that here aswell. + tessCw = !tessCw; + } + + if (tessCw) { context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw); } @@ -375,9 +383,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) { - if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true)) + bool perPatch = dest.Type == OperandType.AttributePerPatch; + + if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch)) { - bool perPatch = dest.Type == OperandType.AttributePerPatch; AggregateType elemType; var elemPointer = perPatch diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 69f9a520..1c329b59 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -306,18 +306,36 @@ namespace Ryujinx.Graphics.Shader.Decoders for (int elemIndex = 0; elemIndex < count; elemIndex++) { int attr = offset + elemIndex * 4; - if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) + + if (perPatch) + { + if (attr >= AttributeConsts.UserAttributePerPatchBase && attr < AttributeConsts.UserAttributePerPatchEnd) + { + int userAttr = attr - AttributeConsts.UserAttributePerPatchBase; + int index = userAttr / 16; + + if (isStore) + { + config.SetOutputUserAttributePerPatch(index); + } + else + { + config.SetInputUserAttributePerPatch(index); + } + } + } + else if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) { int userAttr = attr - AttributeConsts.UserAttributeBase; int index = userAttr / 16; if (isStore) { - config.SetOutputUserAttribute(index, perPatch); + config.SetOutputUserAttribute(index); } else { - config.SetInputUserAttribute(index, (userAttr >> 2) & 3, perPatch); + config.SetInputUserAttribute(index, (userAttr >> 2) & 3); } } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 6ce2e537..7edf5deb 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(offset); - if (op.O) + if (op.O && CanLoadOutput(offset)) { offset |= AttributeConsts.LoadOutputMask; } @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(offset); - if (op.O) + if (op.O && CanLoadOutput(offset)) { offset |= AttributeConsts.LoadOutputMask; } @@ -241,6 +241,11 @@ namespace Ryujinx.Graphics.Shader.Instructions } } + private static bool CanLoadOutput(int attr) + { + return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY; + } + private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr) { if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR) diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 9d8e64bf..85049abb 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -97,7 +97,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant) { int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2); - context.Info.Inputs.Add(attrOffset); + + if ((src1.Value & AttributeConsts.LoadOutputMask) != 0) + { + context.Info.Outputs.Add(attrOffset); + } + else + { + context.Info.Inputs.Add(attrOffset); + } } } diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 0c3ab08e..f4e39d0d 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -54,6 +54,9 @@ namespace Ryujinx.Graphics.Shader.Translation 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; diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index 6680a332..35dd56e8 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -4,36 +4,30 @@ namespace Ryujinx.Graphics.Shader.Translation { struct AttributeInfo { - private static readonly Dictionary BuiltInAttributes = new Dictionary() + private static readonly Dictionary _builtInAttributes = 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) }, - { 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.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | 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.Vector | AggregateType.FP32) }, - { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, - { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, - { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, + { 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.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | 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.Vector | AggregateType.FP32) }, + { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, + { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 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) }, @@ -55,6 +49,16 @@ namespace Ryujinx.Graphics.Shader.Translation { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | 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; } @@ -76,6 +80,11 @@ namespace Ryujinx.Graphics.Shader.Translation 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()) @@ -86,6 +95,11 @@ namespace Ryujinx.Graphics.Shader.Translation 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; @@ -115,7 +129,24 @@ namespace Ryujinx.Graphics.Shader.Translation { return new AttributeInfo(value, 0, 1, AggregateType.FP32); } - else if (BuiltInAttributes.TryGetValue(value, out AttributeInfo info)) + 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.Vector | AggregateType.FP32, false); + } + else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info)) { return info; } diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index e8b682d0..3e50ce2f 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int index = BitOperations.TrailingZeroCount(passthroughAttributes); WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex); - Config.SetOutputUserAttribute(index, perPatch: false); + Config.SetOutputUserAttribute(index); passthroughAttributes &= ~(1 << index); } @@ -364,7 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0; if (targetEnabled) { - Config.SetOutputUserAttribute(rtIndex, perPatch: false); + Config.SetOutputUserAttribute(rtIndex); regIndexBase += 4; } } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 221ca1d4..b18979d8 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -50,16 +50,16 @@ namespace Ryujinx.Graphics.Shader.Translation public bool NextUsesFixedFuncAttributes { get; private set; } public int UsedInputAttributes { get; private set; } public int UsedOutputAttributes { get; private set; } - public int UsedInputAttributesPerPatch { get; private set; } - public int UsedOutputAttributesPerPatch { get; private set; } + public HashSet UsedInputAttributesPerPatch { get; } + public HashSet UsedOutputAttributesPerPatch { get; } + public HashSet NextUsedInputAttributesPerPatch { get; private set; } public int PassthroughAttributes { get; private set; } private int _nextUsedInputAttributes; private int _thisUsedInputAttributes; + private Dictionary _perPatchAttributeLocations; public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; } - public UInt128 NextInputAttributesPerPatchComponents { get; private set; } - public UInt128 ThisInputAttributesPerPatchComponents { get; private set; } private int _usedConstantBuffers; private int _usedStorageBuffers; @@ -119,9 +119,13 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options) { - Stage = ShaderStage.Compute; - GpuAccessor = gpuAccessor; - Options = options; + Stage = ShaderStage.Compute; + GpuAccessor = gpuAccessor; + Options = options; + + UsedInputAttributesPerPatch = new HashSet(); + UsedOutputAttributesPerPatch = new HashSet(); + _usedTextures = new Dictionary(); _usedImages = new Dictionary(); } @@ -244,49 +248,71 @@ namespace Ryujinx.Graphics.Shader.Translation UsedOutputAttributes |= 1 << index; } - public void SetInputUserAttribute(int index, int component, bool perPatch) + public void SetInputUserAttribute(int index, int component) { - if (perPatch) - { - UsedInputAttributesPerPatch |= 1 << index; - ThisInputAttributesPerPatchComponents |= UInt128.Pow2(index * 4 + component); - } - else - { - int mask = 1 << index; + int mask = 1 << index; - UsedInputAttributes |= mask; - _thisUsedInputAttributes |= mask; - ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); - } + UsedInputAttributes |= mask; + _thisUsedInputAttributes |= mask; + ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); } - public void SetOutputUserAttribute(int index, bool perPatch) + public void SetInputUserAttributePerPatch(int index) { - if (perPatch) - { - UsedOutputAttributesPerPatch |= 1 << index; - } - else - { - UsedOutputAttributes |= 1 << index; - } + UsedInputAttributesPerPatch.Add(index); + } + + public void SetOutputUserAttribute(int index) + { + UsedOutputAttributes |= 1 << index; + } + + public void SetOutputUserAttributePerPatch(int index) + { + UsedOutputAttributesPerPatch.Add(index); } public void MergeFromtNextStage(ShaderConfig config) { NextInputAttributesComponents = config.ThisInputAttributesComponents; - NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents; + NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch; NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr); MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch); + if (UsedOutputAttributesPerPatch.Count != 0) + { + // Regular and per-patch input/output locations can't overlap, + // so we must assign on our location using unused regular input/output locations. + + Dictionary locationsMap = new Dictionary(); + + int freeMask = ~UsedOutputAttributes; + + foreach (int attr in UsedOutputAttributesPerPatch) + { + int location = BitOperations.TrailingZeroCount(freeMask); + if (location == 32) + { + config.GpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}."); + break; + } + + locationsMap.Add(attr, location); + freeMask &= ~(1 << location); + } + + // Both stages must agree on the locations, so use the same "map" for both. + _perPatchAttributeLocations = locationsMap; + config._perPatchAttributeLocations = locationsMap; + } + if (config.Stage != ShaderStage.Fragment) { LastInVertexPipeline = false; } } - public void MergeOutputUserAttributes(int mask, int maskPerPatch) + public void MergeOutputUserAttributes(int mask, IEnumerable perPatch) { _nextUsedInputAttributes = mask; @@ -297,10 +323,20 @@ namespace Ryujinx.Graphics.Shader.Translation else { UsedOutputAttributes |= mask; - UsedOutputAttributesPerPatch |= maskPerPatch; + UsedOutputAttributesPerPatch.UnionWith(perPatch); } } + public int GetPerPatchAttributeLocation(int index) + { + if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location)) + { + return index; + } + + return location; + } + public bool IsUsedOutputAttribute(int attr) { // The check for fixed function attributes on the next stage is conservative, diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 8657c0f7..78fd9498 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -204,14 +204,12 @@ namespace Ryujinx.Graphics.Shader.Translation InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false); } - UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents; - while (usedAttributesPerPatch != UInt128.Zero) + if (context.Config.NextUsedInputAttributesPerPatch != null) { - int index = usedAttributesPerPatch.TrailingZeroCount(); - - InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: true); - - usedAttributesPerPatch &= ~UInt128.Pow2(index); + foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.OrderBy(x => x)) + { + InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true); + } } if (config.NextUsesFixedFuncAttributes) @@ -236,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.Translation for (int c = 0; c < 4; c++) { int attrOffset = baseAttr + c * 4; - context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f)); + InitializeOutputComponent(context, attrOffset, perPatch); } } diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 8900f9fe..7d820f03 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; +using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.Translation.Translator; @@ -137,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (other != null) { - other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, 0); + other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty()); FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);