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
         /// <summary>
         /// Version of the codegen (to be changed when codegen or guest format change).
         /// </summary>
-        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);
                         }
                     }
                 }