diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index e5289bc019..51ae5aa4d0 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2542; + private const ulong ShaderCodeGenVersion = 2546; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Constants.cs b/Ryujinx.Graphics.Shader/CodeGen/Constants.cs deleted file mode 100644 index 59e9f14584..0000000000 --- a/Ryujinx.Graphics.Shader/CodeGen/Constants.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen -{ - static class Constants - { - public const int MaxShaderStorageBuffers = 16; - - public const int ConstantBufferSize = 0x10000; // In bytes - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 2a93be323b..7b26801a55 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -402,14 +402,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) { - int usedAttribtes = context.Config.UsedInputAttributes; - while (usedAttribtes != 0) + if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) { - int index = BitOperations.TrailingZeroCount(usedAttribtes); + string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty; - DeclareInputAttribute(context, info, index); + context.AppendLine($"layout (location = 0) in vec4 {DefaultNames.IAttributePrefix}{suffix}[{Constants.MaxAttributes}];"); + } + else + { + int usedAttributes = context.Config.UsedInputAttributes; + while (usedAttributes != 0) + { + int index = BitOperations.TrailingZeroCount(usedAttributes); - usedAttribtes &= ~(1 << index); + DeclareInputAttribute(context, info, index); + + usedAttributes &= ~(1 << index); + } } } @@ -448,14 +457,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) { - int usedAttribtes = context.Config.UsedOutputAttributes; - while (usedAttribtes != 0) + if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing)) { - int index = BitOperations.TrailingZeroCount(usedAttribtes); + context.AppendLine($"layout (location = 0) out vec4 {DefaultNames.OAttributePrefix}[{Constants.MaxAttributes}];"); + } + else + { + int usedAttributes = context.Config.UsedOutputAttributes; + while (usedAttributes != 0) + { + int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareOutputAttribute(context, index); + DeclareOutputAttribute(context, index); - usedAttribtes &= ~(1 << index); + usedAttributes &= ~(1 << index); + } } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 6ea700ac71..2d6ede0a78 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute) { - dest = OperandManager.GetOutAttributeName(operand, context.Config); + dest = OperandManager.GetOutAttributeName(operand.Value, context.Config); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index d5cd0f72e7..41a8b94299 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -161,6 +161,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.PackHalf2x16: return PackHalf2x16(context, operation); + case Instruction.StoreAttribute: + return StoreAttribute(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 f3774a60df..a495765809 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -109,6 +109,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.StoreLocal, InstType.Special); Add(Instruction.StoreShared, InstType.Special); Add(Instruction.StoreStorage, InstType.Special); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index ef3b0bedd3..a0aec28edd 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -137,15 +137,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { IAstNode src1 = operation.GetSource(0); IAstNode src2 = operation.GetSource(1); + IAstNode src3 = operation.GetSource(2); - if (!(src1 is AstOperand attr) || attr.Type != OperandType.Attribute) + if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant) { - throw new InvalidOperationException("First source of LoadAttribute must be a attribute."); + throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand."); } - string indexExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + string indexExpr = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2)); - return OperandManager.GetAttributeName(attr, context.Config, isOutAttr: false, indexExpr); + if (src2 is AstOperand operand && operand.Type == OperandType.Constant) + { + return OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, 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); + } } public static string LoadConstant(CodeGenContext context, AstOperation operation) @@ -154,16 +164,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IAstNode src2 = operation.GetSource(1); string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true); var config = context.Config; bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug(); - if (src1 is AstOperand oper && oper.Type == OperandType.Constant) + if (src1 is AstOperand operand && operand.Type == OperandType.Constant) { bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing); - return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, config.Stage, cbIndexable, indexElement); + return OperandManager.GetConstantBufferName(operand.Value, offsetExpr, config.Stage, cbIndexable, indexElement); } else { @@ -250,6 +259,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}"; } + public static string StoreAttribute(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) + { + attrName = OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, 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}"; + } + public static string StoreLocal(CodeGenContext context, AstOperation operation) { return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 60a471eb92..b9f1b4ef1f 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Attribute => GetAttributeName(operand, config), + OperandType.Attribute => GetAttributeName(operand.Value, config), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.ConstantBuffer => GetConstantBufferName( operand.CbufSlot, @@ -142,15 +142,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } - public static string GetOutAttributeName(AstOperand attr, ShaderConfig config) + public static string GetOutAttributeName(int value, ShaderConfig config) { - return GetAttributeName(attr, config, isOutAttr: true); + return GetAttributeName(value, config, isOutAttr: true); } - public static string GetAttributeName(AstOperand attr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0") + public static string GetAttributeName(int value, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0") { - int value = attr.Value; - char swzMask = GetSwizzleMask((value >> 2) & 3); if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) @@ -161,7 +159,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix; - if ((config.Options.Flags & TranslationFlags.Feedback) != 0) + if (config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing)) + { + string name = prefix; + + if (config.Stage == ShaderStage.Geometry && !isOutAttr) + { + name += $"[{indexExpr}]"; + } + + return name + $"[{(value >> 4)}]." + swzMask; + } + else if (config.Options.Flags.HasFlag(TranslationFlags.Feedback)) { string name = $"{prefix}{(value >> 4)}_{swzMask}"; @@ -231,6 +240,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl 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) @@ -314,12 +337,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { if (node is AstOperation operation) { - // 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.Inst == Instruction.LoadAttribute) { - return GetOperandVarType((AstOperand)operation.GetSource(0)); + // 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) + { + if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) + { + return builtInAttr.Type; + } + } + + return OperandInfo.GetVarType(OperandType.Attribute); } else if (operation.Inst == Instruction.Call) { diff --git a/Ryujinx.Graphics.Shader/Constants.cs b/Ryujinx.Graphics.Shader/Constants.cs new file mode 100644 index 0000000000..86af48cfa6 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Constants.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader +{ + static class Constants + { + public const int ConstantBufferSize = 0x10000; // In bytes + + public const int MaxAttributes = 16; + public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes)); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 12b49d35d8..c2901eab2c 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -282,23 +282,7 @@ namespace Ryujinx.Graphics.Shader.Decoders // Populate used attributes. if (op is IOpCodeAttribute opAttr) { - for (int elemIndex = 0; elemIndex < opAttr.Count; elemIndex++) - { - int attr = opAttr.AttributeOffset + elemIndex * 4; - if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) - { - int index = (attr - AttributeConsts.UserAttributeBase) / 16; - - if (op.Emitter == InstEmit.Ast) - { - config.SetOutputUserAttribute(index); - } - else - { - config.SetInputUserAttribute(index); - } - } - } + SetUserAttributeUses(config, opAttr); } block.OpCodes.Add(op); @@ -310,6 +294,41 @@ namespace Ryujinx.Graphics.Shader.Decoders block.UpdatePushOps(); } + private static void SetUserAttributeUses(ShaderConfig config, IOpCodeAttribute opAttr) + { + if (opAttr.Indexed) + { + if (opAttr.Emitter == InstEmit.Ast) + { + config.SetAllOutputUserAttributes(); + } + else + { + config.SetAllInputUserAttributes(); + } + } + else + { + for (int elemIndex = 0; elemIndex < opAttr.Count; elemIndex++) + { + int attr = opAttr.AttributeOffset + elemIndex * 4; + if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) + { + int index = (attr - AttributeConsts.UserAttributeBase) / 16; + + if (opAttr.Emitter == InstEmit.Ast) + { + config.SetOutputUserAttribute(index); + } + else + { + config.SetInputUserAttribute(index); + } + } + } + } + } + private static bool IsUnconditionalBranch(OpCode opCode) { return IsUnconditional(opCode) && IsControlFlowChange(opCode); diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeAttribute.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAttribute.cs index b5b16f1bd4..2b6835bea7 100644 --- a/Ryujinx.Graphics.Shader/Decoders/IOpCodeAttribute.cs +++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAttribute.cs @@ -1,8 +1,9 @@ namespace Ryujinx.Graphics.Shader.Decoders { - interface IOpCodeAttribute + interface IOpCodeAttribute : IOpCode { int AttributeOffset { get; } int Count { get; } + bool Indexed { get; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAl2p.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAl2p.cs new file mode 100644 index 0000000000..d924b39348 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAl2p.cs @@ -0,0 +1,24 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAl2p : OpCode, IOpCodeRd, IOpCodeRa + { + public Register Rd { get; } + public Register Ra { get; } + public Register Predicate44 { get; } + + public int Immediate { get; } + + public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeAl2p(emitter, address, opCode); + + public OpCodeAl2p(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Predicate44 = new Register(opCode.Extract(44, 3), RegisterType.Predicate); + + Immediate = ((int)opCode << 1) >> 21; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs index f911966501..0a24d4c8be 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs @@ -4,14 +4,19 @@ namespace Ryujinx.Graphics.Shader.Decoders { class OpCodeAttribute : OpCodeAluReg, IOpCodeAttribute { - public int AttributeOffset { get; } - public int Count { get; } + public int AttributeOffset { get; } + public bool Patch { get; } + public int Count { get; } + + public bool Phys => !Patch && AttributeOffset == 0 && !Ra.IsRZ; + public bool Indexed => Phys; public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeAttribute(emitter, address, opCode); public OpCodeAttribute(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { AttributeOffset = opCode.Extract(20, 10); + Patch = opCode.Extract(31); Count = opCode.Extract(47, 2) + 1; } } diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs index dc4e03e3df..d38a1b3afb 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs @@ -7,6 +7,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int AttributeOffset { get; } public int Count => 1; + public bool Idx { get; } + public bool Indexed => Idx; + public InterpolationMode Mode { get; } public new static OpCode Create(InstEmitter emitter, ulong address, long opCode) => new OpCodeIpa(emitter, address, opCode); @@ -15,6 +18,8 @@ namespace Ryujinx.Graphics.Shader.Decoders { AttributeOffset = opCode.Extract(28, 10); + Idx = opCode.Extract(38); + Saturate = opCode.Extract(51); Mode = (InterpolationMode)opCode.Extract(54, 2); diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index 8b9cb20dd8..e5bcd7e5b3 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.Decoders _opCodes = new TableEntry[1 << EncodingBits]; #region Instructions + Set("1110111110100x", InstEmit.Al2p, OpCodeAl2p.Create); Set("1110111111011x", InstEmit.Ald, OpCodeAttribute.Create); Set("1110111111110x", InstEmit.Ast, OpCodeAttribute.Create); Set("11101101xxxxxx", InstEmit.Atom, OpCodeAtom.Create); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 7afdbf4e1a..6744a9e897 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -15,6 +15,18 @@ namespace Ryujinx.Graphics.Shader.Instructions Shared } + public static void Al2p(EmitterContext context) + { + OpCodeAl2p op = (OpCodeAl2p)context.CurrOp; + + if (op.Rd.IsRZ) + { + return; + } + + context.Copy(Register(op.Rd), context.IAdd(Register(op.Ra), Const(op.Immediate))); + } + public static void Ald(EmitterContext context) { OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; @@ -30,11 +42,31 @@ namespace Ryujinx.Graphics.Shader.Instructions break; } - Operand src = Attribute(op.AttributeOffset + index * 4); + if (op.Phys) + { + Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase)); + Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2)); - context.FlagAttributeRead(src.Value); + context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex)); - context.Copy(Register(rd), context.LoadAttribute(src, primVertex)); + context.Config.SetUsedFeature(FeatureFlags.IaIndexing); + } + else if (op.Rc.IsRZ) + { + Operand src = Attribute(op.AttributeOffset + index * 4); + + context.FlagAttributeRead(src.Value); + + context.Copy(Register(rd), src); + } + else + { + Operand src = Const(op.AttributeOffset + index * 4); + + context.FlagAttributeRead(src.Value); + + context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex)); + } } } @@ -51,11 +83,23 @@ namespace Ryujinx.Graphics.Shader.Instructions Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); - Operand dest = Attribute(op.AttributeOffset + index * 4); + if (op.Phys) + { + Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase)); + Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2)); - context.FlagAttributeWritten(dest.Value); + context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd)); - context.Copy(dest, Register(rd)); + context.Config.SetUsedFeature(FeatureFlags.OaIndexing); + } + else + { + Operand dest = Attribute(op.AttributeOffset + index * 4); + + context.FlagAttributeWritten(dest.Value); + + context.Copy(dest, Register(rd)); + } } } @@ -136,16 +180,31 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(op.AttributeOffset); - Operand res = Attribute(op.AttributeOffset); + Operand res; - if (op.AttributeOffset >= AttributeConsts.UserAttributeBase && - op.AttributeOffset < AttributeConsts.UserAttributeEnd) + if (op.Idx) { - int index = (op.AttributeOffset - AttributeConsts.UserAttributeBase) >> 4; + Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase)); + Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2)); - if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective) + res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0)); + res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); + + context.Config.SetUsedFeature(FeatureFlags.IaIndexing); + } + else + { + res = Attribute(op.AttributeOffset); + + if (op.AttributeOffset >= AttributeConsts.UserAttributeBase && + op.AttributeOffset < AttributeConsts.UserAttributeEnd) { - res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); + int index = (op.AttributeOffset - AttributeConsts.UserAttributeBase) >> 4; + + if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective) + { + res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); + } } } diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 13fd55ec5f..c1431ebc30 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -108,6 +108,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ShuffleXor, Sine, SquareRoot, + StoreAttribute, StoreGlobal, StoreLocal, StoreShared, diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 88cfe7299f..33ee26ba0e 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ImageLoad, VariableType.F32); Add(Instruction.ImageStore, VariableType.None); Add(Instruction.IsNan, VariableType.Bool, VariableType.F32); - Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32); + Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32, VariableType.S32); Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32); Add(Instruction.LoadGlobal, VariableType.U32, VariableType.S32, VariableType.S32); Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32); @@ -115,6 +115,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool); Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar); Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.StoreAttribute, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32); Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32); Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.U32); Add(Instruction.StoreShared, VariableType.None, VariableType.S32, VariableType.U32); diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index e5ba04bc36..113ece99c7 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -511,9 +511,9 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.IsNan, Local(), a); } - public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b) + public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b, Operand c) { - return context.Add(Instruction.LoadAttribute, Local(), a, b); + return context.Add(Instruction.LoadAttribute, Local(), a, b, c); } public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) @@ -617,6 +617,11 @@ 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) + { + return context.Add(Instruction.StoreAttribute, null, a, b, c); + } + public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c) { return context.Add(Instruction.StoreGlobal, null, a, b, c); diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index b0c48410b9..1636afd3af 100644 --- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Shader.Translation Bindless = 1 << 2, InstanceId = 1 << 3, - CbIndexing = 1 << 4 + CbIndexing = 1 << 4, + IaIndexing = 1 << 5, + OaIndexing = 1 << 6 } } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 02e995f9a9..f454ceeabc 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -219,6 +219,16 @@ namespace Ryujinx.Graphics.Shader.Translation } } + public void SetAllInputUserAttributes() + { + UsedInputAttributes |= Constants.AllAttributesMask; + } + + public void SetAllOutputUserAttributes() + { + UsedOutputAttributes |= Constants.AllAttributesMask; + } + public void SetClipDistanceWritten(int index) { ClipDistancesWritten |= (byte)(1 << index); diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 47cf0ac813..3c7b3c2bea 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -103,17 +103,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (temp != null) { - // TODO: LoadAttribute should accept any integer value as first argument, - // then we don't need special case here. Right now it expects the first - // operand to be of type "attribute". - if ((operation.Inst & Instruction.Mask) == Instruction.LoadAttribute) - { - operation.TurnIntoCopy(temp); - } - else - { - operation.SetSource(srcIndex, temp); - } + operation.SetSource(srcIndex, temp); } } }