diff options
Diffstat (limited to 'Ryujinx.Graphics.Shader')
24 files changed, 482 insertions, 136 deletions
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index e29ff486..3e2e51b0 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -136,6 +136,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } + else if (context.Config.Stage == ShaderStage.TessellationControl) + { + int threadsPerInputPrimitive = context.Config.ThreadsPerInputPrimitive; + + context.AppendLine($"layout (vertices = {threadsPerInputPrimitive}) out;"); + context.AppendLine(); + } + else if (context.Config.Stage == ShaderStage.TessellationEvaluation) + { + string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl(); + string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl(); + string windingOrder = context.Config.GpuAccessor.QueryTessCw() ? "cw" : "ccw"; + + context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); + context.AppendLine(); + } if (context.Config.UsedInputAttributes != 0 || context.Config.GpPassthrough) { @@ -150,6 +166,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } + + if (context.Config.UsedInputAttributesPerPatch != 0) + { + DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch); + + context.AppendLine(); + } + + if (context.Config.UsedOutputAttributesPerPatch != 0) + { + DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch); + + context.AppendLine(); + } } else { @@ -424,17 +454,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl while (usedAttributes != 0) { int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareInputAttribute(context, info, index); - usedAttributes &= ~(1 << index); } } } + private static void DeclareInputAttributesPerPatch(CodeGenContext context, int usedAttributes) + { + while (usedAttributes != 0) + { + int index = BitOperations.TrailingZeroCount(usedAttributes); + DeclareInputAttributePerPatch(context, index); + usedAttributes &= ~(1 << index); + } + } + private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr) { - string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty; + string suffix = OperandManager.IsArrayAttribute(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty; string iq = string.Empty; if (context.Config.Stage == ShaderStage.Fragment) @@ -465,6 +503,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } + private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr) + { + string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; + + context.AppendLine($"patch in vec4 {name};"); + } + private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) { if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing)) @@ -477,9 +522,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl while (usedAttributes != 0) { int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareOutputAttribute(context, index); - usedAttributes &= ~(1 << index); } } @@ -487,7 +530,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareOutputAttribute(CodeGenContext context, int attr) { - string name = $"{DefaultNames.OAttributePrefix}{attr}"; + string suffix = OperandManager.IsArrayAttribute(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty; + string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}"; if ((context.Config.Options.Flags & TranslationFlags.Feedback) != 0) { @@ -504,6 +548,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, int usedAttributes) + { + while (usedAttributes != 0) + { + int index = BitOperations.TrailingZeroCount(usedAttributes); + DeclareOutputAttributePerPatch(context, index); + usedAttributes &= ~(1 << index); + } + } + + private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr) + { + string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; + + context.AppendLine($"patch out vec4 {name};"); + } + private static void DeclareSupportUniformBlock(CodeGenContext context, bool isFragment, int scaleElements) { if (!isFragment && scaleElements == 0) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index eaf1050c..47350408 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string SamplerNamePrefix = "tex"; public const string ImageNamePrefix = "img"; + public const string PerPatchAttributePrefix = "patch_attr_"; public const string IAttributePrefix = "in_attr"; public const string OAttributePrefix = "out_attr"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 2d6ede0a..077737c8 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -126,9 +126,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string dest; - if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute) + if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute()) { - dest = OperandManager.GetOutAttributeName(operand.Value, context.Config); + bool perPatch = operand.Type == OperandType.AttributePerPatch; + dest = OperandManager.GetOutAttributeName(operand.Value, context.Config, perPatch); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index edaacd3e..5e46bb46 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -200,7 +200,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (src2 is AstOperand operand && operand.Type == OperandType.Constant) { - return OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, isOutAttr: false, indexExpr); + int attrOffset = baseAttr.Value + (operand.Value << 2); + return OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: false, indexExpr); } else { @@ -326,7 +327,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (src2 is AstOperand operand && operand.Type == OperandType.Constant) { - attrName = OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, isOutAttr: true); + int attrOffset = baseAttr.Value + (operand.Value << 2); + attrName = OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: true); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index d35525f9..9680df27 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -29,27 +29,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>() { - { 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.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) }, // Special. { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, @@ -61,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) }, { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) }, { AttributeConsts.LaneId, new BuiltInAttribute(null, VariableType.U32) }, + { AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", VariableType.S32) }, + { AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", VariableType.S32) }, + { AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", VariableType.S32) }, { AttributeConsts.EqMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) }, { AttributeConsts.GtMask, new BuiltInAttribute(null, VariableType.U32) }, @@ -99,19 +108,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Attribute => GetAttributeName(operand.Value, config), + OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false), + OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), - OperandType.ConstantBuffer => GetConstantBufferName( - operand.CbufSlot, - operand.CbufOffset, - config.Stage, - config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)), + OperandType.ConstantBuffer => GetConstantBufferName(operand, config), OperandType.LocalVariable => _locals[operand], OperandType.Undefined => DefaultNames.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") }; } + private static string GetConstantBufferName(AstOperand operand, ShaderConfig config) + { + return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)); + } + public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable) { return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}"; @@ -142,14 +153,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } - public static string GetOutAttributeName(int value, ShaderConfig config) + public static string GetOutAttributeName(int value, ShaderConfig config, bool perPatch) { - return GetAttributeName(value, config, isOutAttr: true); + return GetAttributeName(value, config, perPatch, isOutAttr: true); } - public static string GetAttributeName(int value, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0") + public static string GetAttributeName(int value, ShaderConfig config, bool perPatch, bool isOutAttr = false, string indexExpr = "0") { - value &= ~3; + if ((value & AttributeConsts.LoadOutputMask) != 0) + { + isOutAttr = true; + } + + value &= AttributeConsts.Mask & ~3; char swzMask = GetSwizzleMask((value >> 2) & 3); if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) @@ -160,7 +176,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix; - if (config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing)) + bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing); + + if (!indexable && perPatch) + { + prefix = DefaultNames.PerPatchAttributePrefix; + } + + if (indexable) { string name = prefix; @@ -175,9 +198,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = $"{prefix}{(value >> 4)}_{swzMask}"; - if (config.Stage == ShaderStage.Geometry && !isOutAttr) + if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr)) { - name += $"[{indexExpr}]"; + name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } return name; @@ -186,9 +209,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = $"{prefix}{(value >> 4)}"; - if (config.Stage == ShaderStage.Geometry && !isOutAttr) + if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr)) { - name += $"[{indexExpr}]"; + name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } return name + '.' + swzMask; @@ -250,9 +273,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string name = builtInAttr.Name; - if (config.Stage == ShaderStage.Geometry && (value & AttributeConsts.SpecialMask) == 0 && !isOutAttr) + if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr) && IsArrayBuiltIn(value)) { - name = $"gl_in[{indexExpr}].{name}"; + name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; } return name; @@ -278,6 +301,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]"; } + public static bool IsArrayAttribute(ShaderStage stage, bool isOutAttr) + { + if (isOutAttr) + { + return stage == ShaderStage.TessellationControl; + } + else + { + return stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry; + } + } + + private static bool IsArrayBuiltIn(int attr) + { + if (attr <= AttributeConsts.TessLevelInner1 || + attr == AttributeConsts.TessCoordX || + attr == AttributeConsts.TessCoordY) + { + return false; + } + + return (attr & AttributeConsts.SpecialMask) == 0; + } + public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable) { if (cbIndexable) diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 656e9c44..b446e650 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -262,6 +262,7 @@ namespace Ryujinx.Graphics.Shader.Decoders int count = 1; bool isStore = false; bool indexed = false; + bool perPatch = false; if (name == InstName.Ast) { @@ -269,14 +270,17 @@ namespace Ryujinx.Graphics.Shader.Decoders count = (int)opAst.AlSize + 1; offset = opAst.Imm11; indexed = opAst.Phys; + perPatch = opAst.P; isStore = true; } else if (name == InstName.Ald) { InstAld opAld = new InstAld(opCode); count = (int)opAld.AlSize + 1; - indexed = opAld.Phys; offset = opAld.Imm11; + indexed = opAld.Phys; + perPatch = opAld.P; + isStore = opAld.O; } else /* if (name == InstName.Ipa) */ { @@ -307,11 +311,11 @@ namespace Ryujinx.Graphics.Shader.Decoders if (isStore) { - config.SetOutputUserAttribute(index); + config.SetOutputUserAttribute(index, perPatch); } else { - config.SetInputUserAttribute(index); + config.SetInputUserAttribute(index, perPatch); } } } diff --git a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs index ca4ff12a..b61412c6 100644 --- a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs +++ b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs @@ -5175,8 +5175,8 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); - public int Imm16 => (int)((_opcode >> 20) & 0xFFFF); public bool PredInv => (_opcode & 0x80000) != 0; + public int Imm16 => (int)((_opcode >> 20) & 0xFFFF); public bool WriteCC => (_opcode & 0x800000000000) != 0; public bool DFormat => (_opcode & 0x40000000000000) != 0; public VectorSelect ASelect => (VectorSelect)((int)((_opcode >> 45) & 0x8) | (int)((_opcode >> 36) & 0x7)); @@ -5236,6 +5236,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; + public int Imm16 => (int)((_opcode >> 20) & 0xFFFF); public VectorSelect ASelect => (VectorSelect)((int)((_opcode >> 45) & 0x8) | (int)((_opcode >> 36) & 0x7)); public VectorSelect BSelect => (VectorSelect)((int)((_opcode >> 46) & 0x8) | (int)((_opcode >> 28) & 0x7)); public IComp VComp => (IComp)((int)((_opcode >> 45) & 0x4) | (int)((_opcode >> 43) & 0x3)); diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 6af42cf2..3fdce8ea 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -96,6 +96,21 @@ namespace Ryujinx.Graphics.Shader return InputTopology.Points; } + bool QueryTessCw() + { + return false; + } + + TessPatchType QueryTessPatchType() + { + return TessPatchType.Triangles; + } + + TessSpacing QueryTessSpacing() + { + return TessSpacing.EqualSpacing; + } + TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1) { return TextureFormat.R8G8B8A8Unorm; diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs index 9cc591ca..33c1065a 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs @@ -474,13 +474,6 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.GpuAccessor.Log("Shader instruction Vset is not implemented."); } - public static void Vsetp(EmitterContext context) - { - InstVsetp op = context.GetOp<InstVsetp>(); - - context.Config.GpuAccessor.Log("Shader instruction Vsetp is not implemented."); - } - public static void Vshl(EmitterContext context) { InstVshl op = context.GetOp<InstVshl>(); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index e865caf2..f82b835c 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -40,19 +40,33 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.SetUsedFeature(FeatureFlags.IaIndexing); } - else if (op.SrcB == RegisterConsts.RegisterZeroIndex) + else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P) { - Operand src = Attribute(op.Imm11 + index * 4); + int offset = op.Imm11 + index * 4; - context.FlagAttributeRead(src.Value); + context.FlagAttributeRead(offset); + + if (op.O) + { + offset |= AttributeConsts.LoadOutputMask; + } + + Operand src = op.P ? AttributePerPatch(offset) : Attribute(offset); context.Copy(Register(rd), src); } else { - Operand src = Const(op.Imm11 + index * 4); + int offset = op.Imm11 + index * 4; + + context.FlagAttributeRead(offset); - context.FlagAttributeRead(src.Value); + if (op.O) + { + offset |= AttributeConsts.LoadOutputMask; + } + + Operand src = Const(offset); context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex)); } @@ -83,9 +97,13 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - Operand dest = Attribute(op.Imm11 + index * 4); + // TODO: Support indirect stores using Ra. + + int offset = op.Imm11 + index * 4; + + context.FlagAttributeWritten(offset); - context.FlagAttributeWritten(dest.Value); + Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset); context.Copy(dest, Register(rd)); } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index 245b2253..240fd6b1 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -79,6 +79,10 @@ namespace Ryujinx.Graphics.Shader.Instructions src = Attribute(AttributeConsts.LaneId); break; + case SReg.InvocationId: + src = Attribute(AttributeConsts.InvocationId); + break; + case SReg.YDirection: src = ConstF(1); // TODO: Use value from Y direction GPU register. break; @@ -87,6 +91,22 @@ namespace Ryujinx.Graphics.Shader.Instructions src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0); break; + case SReg.InvocationInfo: + if (context.Config.Stage != ShaderStage.Compute && context.Config.Stage != ShaderStage.Fragment) + { + Operand primitiveId = Attribute(AttributeConsts.PrimitiveId); + Operand patchVerticesIn = Attribute(AttributeConsts.PatchVerticesIn); + + patchVerticesIn = context.ShiftLeft(patchVerticesIn, Const(16)); + + src = context.BitwiseOr(primitiveId, patchVerticesIn); + } + else + { + src = Const(0); + } + break; + case SReg.TId: Operand tidX = Attribute(AttributeConsts.ThreadIdX); Operand tidY = Attribute(AttributeConsts.ThreadIdY); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs index 890b31d6..120d6f22 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs @@ -120,6 +120,68 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(op.Dest), res); } + public static void Vsetp(EmitterContext context) + { + InstVsetp op = context.GetOp<InstVsetp>(); + + Operand srcA = Extend(context, GetSrcReg(context, op.SrcA), op.ASelect); + + Operand srcB; + + if (op.BVideo) + { + srcB = Extend(context, GetSrcReg(context, op.SrcB), op.BSelect); + } + else + { + int imm = op.Imm16; + + if ((op.BSelect & VectorSelect.S8B0) != 0) + { + imm = (imm << 16) >> 16; + } + + srcB = Const(imm); + } + + Operand p0Res; + + bool signedA = (op.ASelect & VectorSelect.S8B0) != 0; + bool signedB = (op.BSelect & VectorSelect.S8B0) != 0; + + if (signedA != signedB) + { + bool a32 = (op.ASelect & ~VectorSelect.S8B0) == VectorSelect.U32; + bool b32 = (op.BSelect & ~VectorSelect.S8B0) == VectorSelect.U32; + + if (!a32 && !b32) + { + // Both values are extended small integer and can always fit in a S32, just do a signed comparison. + p0Res = GetIntComparison(context, op.VComp, srcA, srcB, isSigned: true, extended: false); + } + else + { + // TODO: Mismatching sign case. + p0Res = Const(0); + } + } + else + { + // Sign matches, just do a regular comparison. + p0Res = GetIntComparison(context, op.VComp, srcA, srcB, signedA, extended: false); + } + + Operand p1Res = context.BitwiseNot(p0Res); + + Operand pred = GetPredicate(context, op.SrcPred, op.SrcPredInv); + + p0Res = InstEmitAluHelper.GetPredLogicalOp(context, op.BoolOp, p0Res, pred); + p1Res = InstEmitAluHelper.GetPredLogicalOp(context, op.BoolOp, p1Res, pred); + + context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res); + context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); + } + private static Operand Extend(EmitterContext context, Operand src, VectorSelect type) { return type switch diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index b0db56f0..03badec9 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -161,5 +161,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation return false; } + + public static bool IsTextureQuery(this Instruction inst) + { + inst &= Instruction.Mask; + return inst == Instruction.Lod || inst == Instruction.TextureSize; + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs index 221e278f..7fed861e 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs @@ -15,6 +15,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation 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 3427b103..7566a03f 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs @@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { Argument, Attribute, + AttributePerPatch, Constant, ConstantBuffer, Label, @@ -11,4 +12,12 @@ 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/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs index 95c5731a..e56008f0 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs @@ -19,15 +19,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public static VariableType GetVarType(OperandType type) { - switch (type) + return type switch { - case OperandType.Attribute: return VariableType.F32; - case OperandType.Constant: return VariableType.S32; - case OperandType.ConstantBuffer: return VariableType.F32; - case OperandType.Undefined: return VariableType.S32; - } - - throw new ArgumentException($"Invalid operand type \"{type}\"."); + OperandType.Attribute => VariableType.F32, + OperandType.AttributePerPatch => VariableType.F32, + OperandType.Constant => VariableType.S32, + OperandType.ConstantBuffer => VariableType.F32, + OperandType.Undefined => VariableType.S32, + _ => throw new ArgumentException($"Invalid operand type \"{type}\".") + }; } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index a9e44175..2a39d021 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -282,6 +282,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr 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); + } + return GetOperand(operand); } diff --git a/Ryujinx.Graphics.Shader/TessPatchType.cs b/Ryujinx.Graphics.Shader/TessPatchType.cs new file mode 100644 index 00000000..2361b69f --- /dev/null +++ b/Ryujinx.Graphics.Shader/TessPatchType.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum TessPatchType + { + Isolines = 0, + Triangles = 1, + Quads = 2 + } + + static class TessPatchTypeExtensions + { + public static string ToGlsl(this TessPatchType type) + { + return type switch + { + TessPatchType.Isolines => "isolines", + TessPatchType.Quads => "quads", + _ => "triangles" + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/TessSpacing.cs b/Ryujinx.Graphics.Shader/TessSpacing.cs new file mode 100644 index 00000000..35c44190 --- /dev/null +++ b/Ryujinx.Graphics.Shader/TessSpacing.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum TessSpacing + { + EqualSpacing = 0, + FractionalEventSpacing = 1, + FractionalOddSpacing = 2 + } + + static class TessSpacingExtensions + { + public static string ToGlsl(this TessSpacing spacing) + { + return spacing switch + { + TessSpacing.FractionalEventSpacing => "fractional_even_spacing", + TessSpacing.FractionalOddSpacing => "fractional_odd_spacing", + _ => "equal_spacing" + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 3d0d216e..128013d8 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -2,36 +2,45 @@ namespace Ryujinx.Graphics.Shader.Translation { static class AttributeConsts { - public const int Layer = 0x064; - 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 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 FrontFacing = 0x3fc; + 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 Layer = 0x064; + 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 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 FrontFacing = 0x3fc; public const int UserAttributesCount = 32; public const int UserAttributeBase = 0x80; public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16; + 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 = 0xff << 24; + public const int SpecialMask = 0xf << 24; public const int FragmentOutputDepth = 0x1000000; public const int FragmentOutputColorBase = 0x1000010; public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16; @@ -49,12 +58,16 @@ namespace Ryujinx.Graphics.Shader.Translation public const int LaneId = 0x2000020; - public const int EqMask = 0x2000024; - public const int GeMask = 0x2000028; - public const int GtMask = 0x200002c; - public const int LeMask = 0x2000030; - public const int LtMask = 0x2000034; + public const int InvocationId = 0x2000024; + public const int PrimitiveId = 0x2000028; + public const int PatchVerticesIn = 0x200002c; + + 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 = 0x2000038; + public const int ThreadKill = 0x2000044; } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index bdfd9626..6f2a6c3b 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (target.Enabled) { - Config.SetOutputUserAttribute(rtIndex); + Config.SetOutputUserAttribute(rtIndex, perPatch: false); regIndexBase += 4; } } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 72fa7733..ec7e8982 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Shader.Translation public bool GpPassthrough { get; } + public int ThreadsPerInputPrimitive { get; } + public OutputTopology OutputTopology { get; } public int MaxOutputVertices { get; } @@ -42,7 +44,9 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly TranslationCounts _counts; public int UsedInputAttributes { get; private set; } + public int UsedInputAttributesPerPatch { get; private set; } public int UsedOutputAttributes { get; private set; } + public int UsedOutputAttributesPerPatch { get; private set; } public int PassthroughAttributes { get; private set; } private int _usedConstantBuffers; @@ -111,15 +115,16 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts) : this(gpuAccessor, options, counts) { - Stage = header.Stage; - GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; - OutputTopology = header.OutputTopology; - MaxOutputVertices = header.MaxOutputVertexCount; - LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize; - ImapTypes = header.ImapTypes; - OmapTargets = header.OmapTargets; - OmapSampleMask = header.OmapSampleMask; - OmapDepth = header.OmapDepth; + Stage = header.Stage; + GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; + ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive; + OutputTopology = header.OutputTopology; + MaxOutputVertices = header.MaxOutputVertexCount; + LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize; + ImapTypes = header.ImapTypes; + OmapTargets = header.OmapTargets; + OmapSampleMask = header.OmapSampleMask; + OmapDepth = header.OmapDepth; } public int GetDepthRegister() @@ -169,7 +174,7 @@ namespace Ryujinx.Graphics.Shader.Translation public TextureFormat GetTextureFormatAtomic(int handle, int cbufSlot = -1) { - // Atomic image instructions do not support GL_EXT_shader_image_load_formatted, + // Atomic image instructions do not support GL_EXT_shader_image_load_formatted, // and must have a type specified. Default to R32Sint if not available. var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot); @@ -219,17 +224,31 @@ namespace Ryujinx.Graphics.Shader.Translation } } - public void SetInputUserAttribute(int index) + public void SetInputUserAttribute(int index, bool perPatch) { - UsedInputAttributes |= 1 << index; + if (perPatch) + { + UsedInputAttributesPerPatch |= 1 << index; + } + else + { + UsedInputAttributes |= 1 << index; + } } - public void SetOutputUserAttribute(int index) + public void SetOutputUserAttribute(int index, bool perPatch) { - UsedOutputAttributes |= 1 << index; + if (perPatch) + { + UsedOutputAttributesPerPatch |= 1 << index; + } + else + { + UsedOutputAttributes |= 1 << index; + } } - public void MergeOutputUserAttributes(int mask) + public void MergeOutputUserAttributes(int mask, int maskPerPatch) { if (GpPassthrough) { @@ -238,6 +257,7 @@ namespace Ryujinx.Graphics.Shader.Translation else { UsedOutputAttributes |= mask; + UsedOutputAttributesPerPatch |= maskPerPatch; } } diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 0a0ee4a7..0243eba1 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -216,27 +216,38 @@ namespace Ryujinx.Graphics.Shader.Translation return; } - void InitializeOutput(int baseAttr) + if (config.Stage == ShaderStage.Vertex) { - for (int c = 0; c < 4; c++) - { - context.Copy(Attribute(baseAttr + c * 4), ConstF(c == 3 ? 1f : 0f)); - } + InitializeOutput(context, AttributeConsts.PositionX, perPatch: false); } - if (config.Stage == ShaderStage.Vertex) + int usedAttributes = context.Config.UsedOutputAttributes; + while (usedAttributes != 0) { - InitializeOutput(AttributeConsts.PositionX); + int index = BitOperations.TrailingZeroCount(usedAttributes); + + InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false); + + usedAttributes &= ~(1 << index); } - int usedAttribtes = context.Config.UsedOutputAttributes; - while (usedAttribtes != 0) + int usedAttributesPerPatch = context.Config.UsedOutputAttributesPerPatch; + while (usedAttributesPerPatch != 0) { - int index = BitOperations.TrailingZeroCount(usedAttribtes); + int index = BitOperations.TrailingZeroCount(usedAttributesPerPatch); - InitializeOutput(AttributeConsts.UserAttributeBase + index * 16); + InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: true); - usedAttribtes &= ~(1 << index); + usedAttributesPerPatch &= ~(1 << index); + } + } + + private static void InitializeOutput(EmitterContext context, int baseAttr, bool perPatch) + { + for (int c = 0; c < 4; c++) + { + int attrOffset = baseAttr + c * 4; + context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f)); } } diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 3c7b3c2b..34b116d6 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -32,10 +32,13 @@ namespace Ryujinx.Graphics.Shader.Translation private static bool IsUserAttribute(Operand operand) { - return operand != null && - operand.Type == OperandType.Attribute && - operand.Value >= AttributeConsts.UserAttributeBase && - operand.Value < AttributeConsts.UserAttributeEnd; + if (operand != null && operand.Type.IsAttribute()) + { + int value = operand.Value & AttributeConsts.Mask; + return value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd; + } + + return false; } private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart) @@ -133,14 +136,16 @@ namespace Ryujinx.Graphics.Shader.Translation { if (nextStage != null) { - _config.MergeOutputUserAttributes(nextStage._config.UsedInputAttributes); + _config.MergeOutputUserAttributes( + nextStage._config.UsedInputAttributes, + nextStage._config.UsedInputAttributesPerPatch); } FunctionCode[] code = EmitShader(_cfg, _config, initializeOutputs: other == null, out _); if (other != null) { - other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes); + other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, 0); FunctionCode[] otherCode = EmitShader(other._cfg, other._config, initializeOutputs: true, out int aStart); |
