From 7632a7d6d2cd1fca58dcc714fccac01ec2753547 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 29 Apr 2019 23:27:15 -0300
Subject: [PATCH 01/11] shader_bytecode: Add AL2P decoding

---
 src/video_core/engines/shader_bytecode.h | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e5b4eadea..766bb4f79 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -499,6 +499,11 @@ enum class SystemVariable : u64 {
     CircularQueueEntryAddressHigh = 0x63,
 };
 
+enum class PhysicalAttributeDirection : u64 {
+    Input = 0,
+    Output = 1,
+};
+
 union Instruction {
     Instruction& operator=(const Instruction& instr) {
         value = instr.value;
@@ -811,6 +816,12 @@ union Instruction {
         BitField<20, 24, s64> immediate_offset;
     } stg;
 
+    union {
+        BitField<32, 1, PhysicalAttributeDirection> direction;
+        BitField<47, 3, AttributeSize> size;
+        BitField<20, 11, u64> address;
+    } al2p;
+
     union {
         BitField<0, 3, u64> pred0;
         BitField<3, 3, u64> pred3;
@@ -1374,8 +1385,9 @@ public:
         ST_A,
         ST_L,
         ST_S,
-        LDG, // Load from global memory
-        STG, // Store in global memory
+        LDG,  // Load from global memory
+        STG,  // Store in global memory
+        AL2P, // Transforms attribute memory into physical memory
         TEX,
         TEX_B,  // Texture Load Bindless
         TXQ,    // Texture Query
@@ -1646,6 +1658,7 @@ private:
             INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
             INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
             INST("1110111011011---", Id::STG, Type::Memory, "STG"),
+            INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
             INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
             INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
             INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),

From 002ecbea190de16294d32449c3d2b61e57490dae Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 29 Apr 2019 23:28:28 -0300
Subject: [PATCH 02/11] shader_ir/memory: Emit AL2P IR

---
 src/video_core/shader/decode/memory.cpp | 17 +++++++++++++++++
 src/video_core/shader/shader_ir.h       |  5 +++++
 2 files changed, 22 insertions(+)

diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index ea1092db1..4aa74965f 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -12,6 +12,8 @@
 #include "video_core/engines/shader_bytecode.h"
 #include "video_core/shader/shader_ir.h"
 
+#pragma optimize("", off)
+
 namespace VideoCommon::Shader {
 
 using Tegra::Shader::Attribute;
@@ -239,6 +241,21 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
         }
         break;
     }
+    case OpCode::Id::AL2P: {
+        // Ignore al2p.direction since we don't care about it.
+
+        // Calculate emulation fake physical address.
+        const Node fixed_address{Immediate(static_cast<u32>(instr.al2p.address))};
+        const Node reg{GetRegister(instr.gpr8)};
+        const Node fake_address{Operation(OperationCode::IAdd, NO_PRECISE, reg, fixed_address)};
+
+        // Set the fake address to target register.
+        SetRegister(bb, instr.gpr0, fake_address);
+
+        // Signal the shader IR to declare all possible attributes and varyings
+        use_physical_attributes = true;
+        break;
+    }
     default:
         UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
     }
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 65f1e1de9..b157608b7 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -615,6 +615,10 @@ public:
         return static_cast<std::size_t>(coverage_end * sizeof(u64));
     }
 
+    bool HasPhysicalAttributes() const {
+        return use_physical_attributes;
+    }
+
     const Tegra::Shader::Header& GetHeader() const {
         return header;
     }
@@ -879,6 +883,7 @@ private:
     std::set<Sampler> used_samplers;
     std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
     std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
+    bool use_physical_attributes = true; // Shader uses AL2P
 
     Tegra::Shader::Header header;
 };

From 06b363c9b5ccb64cfe7ac4d001ae35bff30828de Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 29 Apr 2019 23:37:09 -0300
Subject: [PATCH 03/11] shader: Remove unused AbufNode Ipa mode

---
 .../renderer_opengl/gl_shader_decompiler.cpp  |  3 +--
 .../renderer_vulkan/vk_shader_decompiler.cpp  |  7 +++----
 src/video_core/shader/decode/memory.cpp       |  7 ++-----
 src/video_core/shader/decode/other.cpp        |  2 +-
 src/video_core/shader/shader_ir.cpp           | 10 +++-------
 src/video_core/shader/shader_ir.h             | 20 ++++---------------
 6 files changed, 14 insertions(+), 35 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index ef1a1995f..33c0edca9 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -317,8 +317,7 @@ private:
 
     void DeclareInputAttributes() {
         const auto& attributes = ir.GetInputAttributes();
-        for (const auto element : attributes) {
-            const Attribute::Index index = element.first;
+        for (const auto index : attributes) {
             if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
                 // Skip when it's not a generic attribute
                 continue;
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 23d9b10db..850085f35 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -194,8 +194,8 @@ public:
         for (const auto& sampler : ir.GetSamplers()) {
             entries.samplers.emplace_back(sampler);
         }
-        for (const auto& attr : ir.GetInputAttributes()) {
-            entries.attributes.insert(GetGenericAttributeLocation(attr.first));
+        for (const auto& attribute : ir.GetInputAttributes()) {
+            entries.attributes.insert(GetGenericAttributeLocation(attribute));
         }
         entries.clip_distances = ir.GetClipDistances();
         entries.shader_length = ir.GetLength();
@@ -322,8 +322,7 @@ private:
     }
 
     void DeclareInputAttributes() {
-        for (const auto element : ir.GetInputAttributes()) {
-            const Attribute::Index index = element.first;
+        for (const auto index : ir.GetInputAttributes()) {
             if (!IsGenericAttribute(index)) {
                 continue;
             }
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 4aa74965f..84db4d4dc 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -50,16 +50,13 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
         UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
                              "Unaligned attribute loads are not supported");
 
-        Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Pass,
-                                          Tegra::Shader::IpaSampleMode::Default};
-
         u64 next_element = instr.attribute.fmt20.element;
         auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
 
         const auto LoadNextElement = [&](u32 reg_offset) {
             const Node buffer = GetRegister(instr.gpr39);
-            const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index),
-                                                     next_element, input_mode, buffer);
+            const Node attribute =
+                GetInputAttribute(static_cast<Attribute::Index>(next_index), next_element, buffer);
 
             SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
 
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index d750a2936..776bdb931 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -134,7 +134,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
         const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
                                                 instr.ipa.sample_mode.Value()};
 
-        const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode);
+        const Node attr = GetInputAttribute(attribute.index, attribute.element);
         Node value = attr;
         const Tegra::Shader::Attribute::Index index = attribute.index.Value();
         if (index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index e4eb0dfd9..0307ae5b0 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -89,13 +89,9 @@ Node ShaderIR::GetPredicate(bool immediate) {
     return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute));
 }
 
-Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element,
-                                 const Tegra::Shader::IpaMode& input_mode, Node buffer) {
-    const auto [entry, is_new] =
-        used_input_attributes.emplace(std::make_pair(index, std::set<Tegra::Shader::IpaMode>{}));
-    entry->second.insert(input_mode);
-
-    return StoreNode(AbufNode(index, static_cast<u32>(element), input_mode, buffer));
+Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
+    used_input_attributes.emplace(index);
+    return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
 }
 
 Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index b157608b7..6aff64394 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -465,17 +465,9 @@ private:
 /// Attribute buffer memory (known as attributes or varyings in GLSL terms)
 class AbufNode final {
 public:
-    explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
-                                const Tegra::Shader::IpaMode& input_mode, Node buffer = {})
-        : input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {}
-
     explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
                                 Node buffer = {})
-        : input_mode{}, buffer{buffer}, index{index}, element{element} {}
-
-    Tegra::Shader::IpaMode GetInputMode() const {
-        return input_mode;
-    }
+        : buffer{buffer}, index{index}, element{element} {}
 
     Tegra::Shader::Attribute::Index GetIndex() const {
         return index;
@@ -490,7 +482,6 @@ public:
     }
 
 private:
-    const Tegra::Shader::IpaMode input_mode;
     const Node buffer;
     const Tegra::Shader::Attribute::Index index;
     const u32 element;
@@ -585,8 +576,7 @@ public:
         return used_predicates;
     }
 
-    const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>&
-    GetInputAttributes() const {
+    const std::set<Tegra::Shader::Attribute::Index>& GetInputAttributes() const {
         return used_input_attributes;
     }
 
@@ -700,8 +690,7 @@ private:
     /// Generates a predicate node for an immediate true or false value
     Node GetPredicate(bool immediate);
     /// Generates a node representing an input attribute. Keeps track of used attributes.
-    Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element,
-                           const Tegra::Shader::IpaMode& input_mode, Node buffer = {});
+    Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer = {});
     /// Generates a node representing an output attribute. Keeps track of used attributes.
     Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
     /// Generates a node representing an internal flag
@@ -876,8 +865,7 @@ private:
 
     std::set<u32> used_registers;
     std::set<Tegra::Shader::Pred> used_predicates;
-    std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>
-        used_input_attributes;
+    std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
     std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
     std::map<u32, ConstBuffer> used_cbufs;
     std::set<Sampler> used_samplers;

From bd81a03d9d4b3e7d69eb8377b13278944bc5ab0b Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 30 Apr 2019 00:09:51 -0300
Subject: [PATCH 04/11] gl_shader_decompiler: Declare all possible varyings on
 physical attribute usage

---
 src/video_core/engines/maxwell_3d.h           |  1 +
 src/video_core/renderer_opengl/gl_device.cpp  |  9 ++
 src/video_core/renderer_opengl/gl_device.h    | 14 ++-
 .../renderer_opengl/gl_shader_decompiler.cpp  | 91 +++++++++++++------
 4 files changed, 88 insertions(+), 27 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 85d309d9b..b1e640dd1 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -51,6 +51,7 @@ public:
         static constexpr std::size_t NumCBData = 16;
         static constexpr std::size_t NumVertexArrays = 32;
         static constexpr std::size_t NumVertexAttributes = 32;
+        static constexpr std::size_t NumVaryings = 31;
         static constexpr std::size_t NumTextureSamplers = 32;
         static constexpr std::size_t NumClipDistances = 8;
         static constexpr std::size_t MaxShaderProgram = 6;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index b6d9e0ddb..38497678a 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -21,9 +21,18 @@ T GetInteger(GLenum pname) {
 
 Device::Device() {
     uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
+    max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
+    max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
     has_variable_aoffi = TestVariableAoffi();
 }
 
+Device::Device(std::nullptr_t) {
+    uniform_buffer_alignment = 0;
+    max_vertex_attributes = 16;
+    max_varyings = 15;
+    has_variable_aoffi = true;
+}
+
 bool Device::TestVariableAoffi() {
     const GLchar* AOFFI_TEST = R"(#version 430 core
 uniform sampler2D tex;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 78ff5ee58..de8490682 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -5,17 +5,27 @@
 #pragma once
 
 #include <cstddef>
+#include "common/common_types.h"
 
 namespace OpenGL {
 
 class Device {
 public:
-    Device();
+    explicit Device();
+    explicit Device(std::nullptr_t);
 
     std::size_t GetUniformBufferAlignment() const {
         return uniform_buffer_alignment;
     }
 
+    u32 GetMaxVertexAttributes() const {
+        return max_vertex_attributes;
+    }
+
+    u32 GetMaxVaryings() const {
+        return max_varyings;
+    }
+
     bool HasVariableAoffi() const {
         return has_variable_aoffi;
     }
@@ -24,6 +34,8 @@ private:
     static bool TestVariableAoffi();
 
     std::size_t uniform_buffer_alignment{};
+    u32 max_vertex_attributes{};
+    u32 max_varyings{};
     bool has_variable_aoffi{};
 };
 
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 33c0edca9..8df91a4c6 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -316,55 +316,85 @@ private:
     }
 
     void DeclareInputAttributes() {
+        if (ir.HasPhysicalAttributes()) {
+            const u32 num_inputs{stage == ShaderStage::Vertex ? GetNumPhysicalAttributes()
+                                                              : GetNumPhysicalVaryings()};
+            for (u32 i = 0; i < num_inputs; ++i) {
+                constexpr auto generic_base{static_cast<u32>(Attribute::Index::Attribute_0)};
+                const auto index{static_cast<Attribute::Index>(generic_base + i)};
+                DeclareInputAttribute(index);
+            }
+            code.AddNewLine();
+            return;
+        }
+
         const auto& attributes = ir.GetInputAttributes();
         for (const auto index : attributes) {
             if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
                 // Skip when it's not a generic attribute
                 continue;
             }
-
-            // TODO(bunnei): Use proper number of elements for these
-            u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
-            if (stage != ShaderStage::Vertex) {
-                // If inputs are varyings, add an offset
-                idx += GENERIC_VARYING_START_LOCATION;
-            }
-
-            std::string attr = GetInputAttribute(index);
-            if (stage == ShaderStage::Geometry) {
-                attr = "gs_" + attr + "[]";
-            }
-            std::string suffix;
-            if (stage == ShaderStage::Fragment) {
-                const auto input_mode =
-                    header.ps.GetAttributeUse(idx - GENERIC_VARYING_START_LOCATION);
-                suffix = GetInputFlags(input_mode);
-            }
-            code.AddLine("layout (location = " + std::to_string(idx) + ") " + suffix + "in vec4 " +
-                         attr + ';');
+            DeclareInputAttribute(index);
         }
         if (!attributes.empty())
             code.AddNewLine();
     }
 
+    void DeclareInputAttribute(Attribute::Index index) {
+        const u32 generic_index{static_cast<u32>(index) -
+                                static_cast<u32>(Attribute::Index::Attribute_0)};
+
+        std::string name{GetInputAttribute(index)};
+        if (stage == ShaderStage::Geometry) {
+            name = "gs_" + name + "[]";
+        }
+        std::string suffix;
+        if (stage == ShaderStage::Fragment) {
+            const auto input_mode{header.ps.GetAttributeUse(generic_index)};
+            suffix = GetInputFlags(input_mode);
+        }
+
+        u32 location = generic_index;
+        if (stage != ShaderStage::Vertex) {
+            // If inputs are varyings, add an offset
+            location += GENERIC_VARYING_START_LOCATION;
+        }
+
+        code.AddLine("layout (location = " + std::to_string(location) + ") " + suffix + "in vec4 " +
+                     name + ';');
+    }
+
     void DeclareOutputAttributes() {
+        if (ir.HasPhysicalAttributes()) {
+            for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
+                constexpr auto generic_base{static_cast<u32>(Attribute::Index::Attribute_0)};
+                const auto index{static_cast<Attribute::Index>(generic_base + i)};
+                DeclareOutputAttribute(index);
+            }
+            code.AddNewLine();
+            return;
+        }
+
         const auto& attributes = ir.GetOutputAttributes();
         for (const auto index : attributes) {
             if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
                 // Skip when it's not a generic attribute
                 continue;
             }
-            // TODO(bunnei): Use proper number of elements for these
-            const auto idx = static_cast<u32>(index) -
-                             static_cast<u32>(Attribute::Index::Attribute_0) +
-                             GENERIC_VARYING_START_LOCATION;
-            code.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
-                         GetOutputAttribute(index) + ';');
+            DeclareOutputAttribute(index);
         }
         if (!attributes.empty())
             code.AddNewLine();
     }
 
+    void DeclareOutputAttribute(Attribute::Index index) {
+        const auto location{static_cast<u32>(index) -
+                            static_cast<u32>(Attribute::Index::Attribute_0) +
+                            GENERIC_VARYING_START_LOCATION};
+        code.AddLine("layout (location = " + std::to_string(location) + ") out vec4 " +
+                     GetOutputAttribute(index) + ';');
+    }
+
     void DeclareConstantBuffers() {
         for (const auto& entry : ir.GetConstantBuffers()) {
             const auto [index, size] = entry;
@@ -1650,6 +1680,15 @@ private:
         return name + '_' + std::to_string(index) + '_' + suffix;
     }
 
+    u32 GetNumPhysicalAttributes() const {
+        return std::min<u32>(device.GetMaxVertexAttributes(), Maxwell::NumVertexAttributes);
+    }
+
+    u32 GetNumPhysicalVaryings() const {
+        return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION,
+                             Maxwell::NumVaryings);
+    }
+
     const Device& device;
     const ShaderIR& ir;
     const ShaderStage stage;

From b7d412c99bb42291dca616910a269dabca7a3938 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 30 Apr 2019 00:18:28 -0300
Subject: [PATCH 05/11] gl_shader_decompiler: Abstract generic attribute
 operations

---
 .../renderer_opengl/gl_shader_decompiler.cpp  | 55 +++++++++----------
 1 file changed, 26 insertions(+), 29 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 8df91a4c6..1ad33107d 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -134,6 +134,19 @@ bool IsPrecise(Node node) {
     return false;
 }
 
+constexpr bool IsGenericAttribute(Attribute::Index index) {
+    return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
+}
+
+constexpr Attribute::Index ToGenericAttribute(u32 value) {
+    return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0));
+}
+
+u32 GetGenericAttributeIndex(Attribute::Index index) {
+    ASSERT(IsGenericAttribute(index));
+    return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
+}
+
 class GLSLDecompiler final {
 public:
     explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
@@ -320,9 +333,7 @@ private:
             const u32 num_inputs{stage == ShaderStage::Vertex ? GetNumPhysicalAttributes()
                                                               : GetNumPhysicalVaryings()};
             for (u32 i = 0; i < num_inputs; ++i) {
-                constexpr auto generic_base{static_cast<u32>(Attribute::Index::Attribute_0)};
-                const auto index{static_cast<Attribute::Index>(generic_base + i)};
-                DeclareInputAttribute(index);
+                DeclareInputAttribute(ToGenericAttribute(i));
             }
             code.AddNewLine();
             return;
@@ -330,24 +341,22 @@ private:
 
         const auto& attributes = ir.GetInputAttributes();
         for (const auto index : attributes) {
-            if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
-                // Skip when it's not a generic attribute
-                continue;
+            if (IsGenericAttribute(index)) {
+                DeclareInputAttribute(index);
             }
-            DeclareInputAttribute(index);
         }
         if (!attributes.empty())
             code.AddNewLine();
     }
 
     void DeclareInputAttribute(Attribute::Index index) {
-        const u32 generic_index{static_cast<u32>(index) -
-                                static_cast<u32>(Attribute::Index::Attribute_0)};
+        const u32 generic_index{GetGenericAttributeIndex(index)};
 
         std::string name{GetInputAttribute(index)};
         if (stage == ShaderStage::Geometry) {
             name = "gs_" + name + "[]";
         }
+
         std::string suffix;
         if (stage == ShaderStage::Fragment) {
             const auto input_mode{header.ps.GetAttributeUse(generic_index)};
@@ -367,9 +376,7 @@ private:
     void DeclareOutputAttributes() {
         if (ir.HasPhysicalAttributes()) {
             for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
-                constexpr auto generic_base{static_cast<u32>(Attribute::Index::Attribute_0)};
-                const auto index{static_cast<Attribute::Index>(generic_base + i)};
-                DeclareOutputAttribute(index);
+                DeclareOutputAttribute(ToGenericAttribute(i));
             }
             code.AddNewLine();
             return;
@@ -377,20 +384,16 @@ private:
 
         const auto& attributes = ir.GetOutputAttributes();
         for (const auto index : attributes) {
-            if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
-                // Skip when it's not a generic attribute
-                continue;
+            if (IsGenericAttribute(index)) {
+                DeclareOutputAttribute(index);
             }
-            DeclareOutputAttribute(index);
         }
         if (!attributes.empty())
             code.AddNewLine();
     }
 
     void DeclareOutputAttribute(Attribute::Index index) {
-        const auto location{static_cast<u32>(index) -
-                            static_cast<u32>(Attribute::Index::Attribute_0) +
-                            GENERIC_VARYING_START_LOCATION};
+        const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION};
         code.AddLine("layout (location = " + std::to_string(location) + ") out vec4 " +
                      GetOutputAttribute(index) + ';');
     }
@@ -569,8 +572,7 @@ private:
                 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
                 return "0";
             default:
-                if (attribute >= Attribute::Index::Attribute_0 &&
-                    attribute <= Attribute::Index::Attribute_31) {
+                if (IsGenericAttribute(attribute)) {
                     return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
                 }
                 break;
@@ -873,8 +875,7 @@ private:
                 case Attribute::Index::ClipDistances4567:
                     return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']';
                 default:
-                    if (attribute >= Attribute::Index::Attribute_0 &&
-                        attribute <= Attribute::Index::Attribute_31) {
+                    if (IsGenericAttribute(attribute)) {
                         return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
                     }
                     UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
@@ -1631,15 +1632,11 @@ private:
     }
 
     std::string GetInputAttribute(Attribute::Index attribute) const {
-        const auto index{static_cast<u32>(attribute) -
-                         static_cast<u32>(Attribute::Index::Attribute_0)};
-        return GetDeclarationWithSuffix(index, "input_attr");
+        return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
     }
 
     std::string GetOutputAttribute(Attribute::Index attribute) const {
-        const auto index{static_cast<u32>(attribute) -
-                         static_cast<u32>(Attribute::Index::Attribute_0)};
-        return GetDeclarationWithSuffix(index, "output_attr");
+        return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
     }
 
     std::string GetConstBuffer(u32 index) const {

From 71aa9d08772eb07ccae7b141e032e6e7e57871a1 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 30 Apr 2019 18:12:30 -0300
Subject: [PATCH 06/11] shader_ir/memory: Implement physical input attributes

---
 src/video_core/engines/shader_bytecode.h |  4 ++++
 src/video_core/shader/decode/memory.cpp  |  9 ++++++---
 src/video_core/shader/shader_ir.cpp      |  5 +++++
 src/video_core/shader/shader_ir.h        | 20 +++++++++++++++++---
 4 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 766bb4f79..e4a9471b8 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -98,6 +98,10 @@ union Attribute {
         BitField<22, 2, u64> element;
         BitField<24, 6, Index> index;
         BitField<47, 3, AttributeSize> size;
+
+        bool IsPhysical() const {
+            return element == 0 && static_cast<u64>(index.Value()) == 0;
+        }
     } fmt20;
 
     union {
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 84db4d4dc..339692295 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -50,13 +50,16 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
         UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
                              "Unaligned attribute loads are not supported");
 
+        const Node buffer{GetRegister(instr.gpr39)};
+
         u64 next_element = instr.attribute.fmt20.element;
         auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
 
         const auto LoadNextElement = [&](u32 reg_offset) {
-            const Node buffer = GetRegister(instr.gpr39);
-            const Node attribute =
-                GetInputAttribute(static_cast<Attribute::Index>(next_index), next_element, buffer);
+            const Node attribute{instr.attribute.fmt20.IsPhysical()
+                                     ? GetPhysicalInputAttribute(instr.gpr8, buffer)
+                                     : GetInputAttribute(static_cast<Attribute::Index>(next_index),
+                                                         next_element, buffer)};
 
             SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
 
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 0307ae5b0..947a372a2 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -94,6 +94,11 @@ Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffe
     return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
 }
 
+Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
+    use_physical_attributes = true;
+    return StoreNode(AbufNode(GetRegister(physical_address), buffer));
+}
+
 Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
     if (index == Attribute::Index::ClipDistances0123 ||
         index == Attribute::Index::ClipDistances4567) {
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 6aff64394..3a1164d4f 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -469,6 +469,9 @@ public:
                                 Node buffer = {})
         : buffer{buffer}, index{index}, element{element} {}
 
+    explicit constexpr AbufNode(Node physical_address, Node buffer = {})
+        : physical_address{physical_address}, buffer{buffer} {}
+
     Tegra::Shader::Attribute::Index GetIndex() const {
         return index;
     }
@@ -481,10 +484,19 @@ public:
         return buffer;
     }
 
+    bool IsPhysicalBuffer() const {
+        return physical_address != nullptr;
+    }
+
+    Node GetPhysicalAddress() const {
+        return physical_address;
+    }
+
 private:
-    const Node buffer;
-    const Tegra::Shader::Attribute::Index index;
-    const u32 element;
+    Node physical_address{};
+    Node buffer{};
+    Tegra::Shader::Attribute::Index index{};
+    u32 element{};
 };
 
 /// Constant buffer node, usually mapped to uniform buffers in GLSL
@@ -691,6 +703,8 @@ private:
     Node GetPredicate(bool immediate);
     /// Generates a node representing an input attribute. Keeps track of used attributes.
     Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer = {});
+    /// Generates a node representing a physical input attribute.
+    Node GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer = {});
     /// Generates a node representing an output attribute. Keeps track of used attributes.
     Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
     /// Generates a node representing an internal flag

From c6f9e651b21aca5ec5afef1f217b39a3b85518b9 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 30 Apr 2019 19:36:18 -0300
Subject: [PATCH 07/11] gl_shader_decompiler: Implement GLSL physical
 attributes

---
 .../renderer_opengl/gl_shader_decompiler.cpp  | 165 +++++++++++-------
 src/video_core/shader/shader_ir.h             |   2 +-
 2 files changed, 101 insertions(+), 66 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 1ad33107d..47bfa5538 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -165,6 +165,7 @@ public:
         DeclareConstantBuffers();
         DeclareGlobalMemory();
         DeclareSamplers();
+        DeclarePhysicalAttributeReader();
 
         code.AddLine("void execute_" + suffix + "() {");
         ++code.scope;
@@ -330,8 +331,7 @@ private:
 
     void DeclareInputAttributes() {
         if (ir.HasPhysicalAttributes()) {
-            const u32 num_inputs{stage == ShaderStage::Vertex ? GetNumPhysicalAttributes()
-                                                              : GetNumPhysicalVaryings()};
+            const u32 num_inputs{GetNumPhysicalInputAttributes()};
             for (u32 i = 0; i < num_inputs; ++i) {
                 DeclareInputAttribute(ToGenericAttribute(i));
             }
@@ -374,7 +374,7 @@ private:
     }
 
     void DeclareOutputAttributes() {
-        if (ir.HasPhysicalAttributes()) {
+        if (ir.HasPhysicalAttributes() && stage != ShaderStage::Fragment) {
             for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
                 DeclareOutputAttribute(ToGenericAttribute(i));
             }
@@ -461,6 +461,31 @@ private:
             code.AddNewLine();
     }
 
+    void DeclarePhysicalAttributeReader() {
+        if (!ir.HasPhysicalAttributes()) {
+            return;
+        }
+        code.AddLine("float readPhysicalAttribute(uint physical_address) {");
+        ++code.scope;
+        code.AddLine("switch (physical_address) {");
+
+        // Just declare generic attributes for now.
+        const auto num_attributes{static_cast<u32>(GetNumPhysicalAttributes())};
+        for (u32 index = 0; index < num_attributes; ++index) {
+            for (u32 element = 0; element < 4; ++element) {
+                code.AddLine(fmt::format("case 0x{:x}: return {};", 0x80 + index * 16 + element * 4,
+                                         ReadAttribute(ToGenericAttribute(index), element)));
+            }
+        }
+
+        code.AddLine("default: return 0;");
+
+        code.AddLine('}');
+        --code.scope;
+        code.AddLine('}');
+        code.AddNewLine();
+    }
+
     void VisitBlock(const NodeBlock& bb) {
         for (const Node node : bb) {
             if (const std::string expr = Visit(node); !expr.empty()) {
@@ -515,69 +540,12 @@ private:
             return value;
 
         } else if (const auto abuf = std::get_if<AbufNode>(node)) {
-            const auto attribute = abuf->GetIndex();
-            const auto element = abuf->GetElement();
-
-            const auto GeometryPass = [&](const std::string& name) {
-                if (stage == ShaderStage::Geometry && abuf->GetBuffer()) {
-                    // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
-                    // set an 0x80000000 index for those and the shader fails to build. Find out why
-                    // this happens and what's its intent.
-                    return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) +
-                           ") % MAX_VERTEX_INPUT]";
-                }
-                return name;
-            };
-
-            switch (attribute) {
-            case Attribute::Index::Position:
-                if (stage != ShaderStage::Fragment) {
-                    return GeometryPass("position") + GetSwizzle(element);
-                } else {
-                    return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
-                }
-            case Attribute::Index::PointCoord:
-                switch (element) {
-                case 0:
-                    return "gl_PointCoord.x";
-                case 1:
-                    return "gl_PointCoord.y";
-                case 2:
-                case 3:
-                    return "0";
-                }
-                UNREACHABLE();
-                return "0";
-            case Attribute::Index::TessCoordInstanceIDVertexID:
-                // TODO(Subv): Find out what the values are for the first two elements when inside a
-                // vertex shader, and what's the value of the fourth element when inside a Tess Eval
-                // shader.
-                ASSERT(stage == ShaderStage::Vertex);
-                switch (element) {
-                case 2:
-                    // Config pack's first value is instance_id.
-                    return "uintBitsToFloat(config_pack[0])";
-                case 3:
-                    return "uintBitsToFloat(gl_VertexID)";
-                }
-                UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
-                return "0";
-            case Attribute::Index::FrontFacing:
-                // TODO(Subv): Find out what the values are for the other elements.
-                ASSERT(stage == ShaderStage::Fragment);
-                switch (element) {
-                case 3:
-                    return "itof(gl_FrontFacing ? -1 : 0)";
-                }
-                UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
-                return "0";
-            default:
-                if (IsGenericAttribute(attribute)) {
-                    return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
-                }
-                break;
+            UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
+                                 "Physical attributes in geometry shaders are not implemented");
+            if (abuf->IsPhysicalBuffer()) {
+                return "readPhysicalAttribute(ftou(" + Visit(abuf->GetPhysicalAddress()) + "))";
             }
-            UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
+            return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
 
         } else if (const auto cbuf = std::get_if<CbufNode>(node)) {
             const Node offset = cbuf->GetOffset();
@@ -629,6 +597,69 @@ private:
         return {};
     }
 
+    std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
+        const auto GeometryPass = [&](std::string name) {
+            if (stage == ShaderStage::Geometry && buffer) {
+                // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
+                // set an 0x80000000 index for those and the shader fails to build. Find out why
+                // this happens and what's its intent.
+                return "gs_" + std::move(name) + "[ftou(" + Visit(buffer) + ") % MAX_VERTEX_INPUT]";
+            }
+            return name;
+        };
+
+        switch (attribute) {
+        case Attribute::Index::Position:
+            if (stage != ShaderStage::Fragment) {
+                return GeometryPass("position") + GetSwizzle(element);
+            } else {
+                return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
+            }
+        case Attribute::Index::PointCoord:
+            switch (element) {
+            case 0:
+                return "gl_PointCoord.x";
+            case 1:
+                return "gl_PointCoord.y";
+            case 2:
+            case 3:
+                return "0";
+            }
+            UNREACHABLE();
+            return "0";
+        case Attribute::Index::TessCoordInstanceIDVertexID:
+            // TODO(Subv): Find out what the values are for the first two elements when inside a
+            // vertex shader, and what's the value of the fourth element when inside a Tess Eval
+            // shader.
+            ASSERT(stage == ShaderStage::Vertex);
+            switch (element) {
+            case 2:
+                // Config pack's first value is instance_id.
+                return "uintBitsToFloat(config_pack[0])";
+            case 3:
+                return "uintBitsToFloat(gl_VertexID)";
+            }
+            UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
+            return "0";
+        case Attribute::Index::FrontFacing:
+            // TODO(Subv): Find out what the values are for the other elements.
+            ASSERT(stage == ShaderStage::Fragment);
+            switch (element) {
+            case 3:
+                return "itof(gl_FrontFacing ? -1 : 0)";
+            }
+            UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
+            return "0";
+        default:
+            if (IsGenericAttribute(attribute)) {
+                return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
+            }
+            break;
+        }
+        UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
+        return "0";
+    }
+
     std::string ApplyPrecise(Operation operation, const std::string& value) {
         if (!IsPrecise(operation)) {
             return value;
@@ -1677,6 +1708,10 @@ private:
         return name + '_' + std::to_string(index) + '_' + suffix;
     }
 
+    u32 GetNumPhysicalInputAttributes() const {
+        return stage == ShaderStage::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
+    }
+
     u32 GetNumPhysicalAttributes() const {
         return std::min<u32>(device.GetMaxVertexAttributes(), Maxwell::NumVertexAttributes);
     }
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 3a1164d4f..a4bb0c41c 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -885,7 +885,7 @@ private:
     std::set<Sampler> used_samplers;
     std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
     std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
-    bool use_physical_attributes = true; // Shader uses AL2P
+    bool use_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
 
     Tegra::Shader::Header header;
 };

From fe700e1856fa078ba0fc93ced8576f5023f3146a Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 30 Apr 2019 19:46:49 -0300
Subject: [PATCH 08/11] shader: Add physical attributes commentaries

---
 src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 2 ++
 src/video_core/shader/decode/memory.cpp                 | 2 +-
 src/video_core/shader/shader_ir.cpp                     | 2 +-
 src/video_core/shader/shader_ir.h                       | 6 ++++--
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 47bfa5538..52552333f 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -895,6 +895,8 @@ private:
             target = GetRegister(gpr->GetIndex());
 
         } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
+            UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
+
             target = [&]() -> std::string {
                 switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
                 case Attribute::Index::Position:
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 339692295..c4f68f8ab 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -253,7 +253,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
         SetRegister(bb, instr.gpr0, fake_address);
 
         // Signal the shader IR to declare all possible attributes and varyings
-        use_physical_attributes = true;
+        uses_physical_attributes = true;
         break;
     }
     default:
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 947a372a2..691d095c8 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -95,7 +95,7 @@ Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffe
 }
 
 Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
-    use_physical_attributes = true;
+    uses_physical_attributes = true;
     return StoreNode(AbufNode(GetRegister(physical_address), buffer));
 }
 
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index a4bb0c41c..7e54f7e74 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -465,10 +465,12 @@ private:
 /// Attribute buffer memory (known as attributes or varyings in GLSL terms)
 class AbufNode final {
 public:
+    // Initialize for standard attributes (index is explicit).
     explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
                                 Node buffer = {})
         : buffer{buffer}, index{index}, element{element} {}
 
+    // Initialize for physical attributes (index is a variable value).
     explicit constexpr AbufNode(Node physical_address, Node buffer = {})
         : physical_address{physical_address}, buffer{buffer} {}
 
@@ -618,7 +620,7 @@ public:
     }
 
     bool HasPhysicalAttributes() const {
-        return use_physical_attributes;
+        return uses_physical_attributes;
     }
 
     const Tegra::Shader::Header& GetHeader() const {
@@ -885,7 +887,7 @@ private:
     std::set<Sampler> used_samplers;
     std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
     std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
-    bool use_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
+    bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
 
     Tegra::Shader::Header header;
 };

From 28bffb1ffa2585429d63c6dab8584b8e46179696 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 30 Apr 2019 20:11:41 -0300
Subject: [PATCH 09/11] shader_ir/memory: Assert on non-32 bits ALD.PHYS

---
 src/video_core/shader/decode/memory.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index c4f68f8ab..6a992c543 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -49,6 +49,9 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
                              "Indirect attribute loads are not supported");
         UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
                              "Unaligned attribute loads are not supported");
+        UNIMPLEMENTED_IF_MSG(instr.attribute.fmt20.IsPhysical() &&
+                                 instr.attribute.fmt20.size != Tegra::Shader::AttributeSize::Word,
+                             "Non-32 bits PHYS reads are not implemented");
 
         const Node buffer{GetRegister(instr.gpr39)};
 

From 5321cdd276830699c50053dd464e66680f4c8a64 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 May 2019 16:06:56 -0300
Subject: [PATCH 10/11] gl_shader_decompiler: Skip physical unused attributes

---
 .../renderer_opengl/gl_shader_decompiler.cpp  | 45 +++++++++++--------
 1 file changed, 27 insertions(+), 18 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 52552333f..da925372c 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -310,30 +310,28 @@ private:
     }
 
     std::string GetInputFlags(AttributeUse attribute) {
-        std::string out;
-
         switch (attribute) {
-        case AttributeUse::Constant:
-            out += "flat ";
-            break;
-        case AttributeUse::ScreenLinear:
-            out += "noperspective ";
-            break;
         case AttributeUse::Perspective:
             // Default, Smooth
-            break;
+            return {};
+        case AttributeUse::Constant:
+            return "flat ";
+        case AttributeUse::ScreenLinear:
+            return "noperspective ";
         default:
-            LOG_CRITICAL(HW_GPU, "Unused attribute being fetched");
-            UNREACHABLE();
+        case AttributeUse::Unused:
+            UNREACHABLE_MSG("Unused attribute being fetched");
+            return {};
+            UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
+            return {};
         }
-        return out;
     }
 
     void DeclareInputAttributes() {
         if (ir.HasPhysicalAttributes()) {
             const u32 num_inputs{GetNumPhysicalInputAttributes()};
             for (u32 i = 0; i < num_inputs; ++i) {
-                DeclareInputAttribute(ToGenericAttribute(i));
+                DeclareInputAttribute(ToGenericAttribute(i), true);
             }
             code.AddNewLine();
             return;
@@ -342,14 +340,14 @@ private:
         const auto& attributes = ir.GetInputAttributes();
         for (const auto index : attributes) {
             if (IsGenericAttribute(index)) {
-                DeclareInputAttribute(index);
+                DeclareInputAttribute(index, false);
             }
         }
         if (!attributes.empty())
             code.AddNewLine();
     }
 
-    void DeclareInputAttribute(Attribute::Index index) {
+    void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
         const u32 generic_index{GetGenericAttributeIndex(index)};
 
         std::string name{GetInputAttribute(index)};
@@ -360,6 +358,9 @@ private:
         std::string suffix;
         if (stage == ShaderStage::Fragment) {
             const auto input_mode{header.ps.GetAttributeUse(generic_index)};
+            if (skip_unused && input_mode == AttributeUse::Unused) {
+                return;
+            }
             suffix = GetInputFlags(input_mode);
         }
 
@@ -470,11 +471,19 @@ private:
         code.AddLine("switch (physical_address) {");
 
         // Just declare generic attributes for now.
-        const auto num_attributes{static_cast<u32>(GetNumPhysicalAttributes())};
+        const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())};
         for (u32 index = 0; index < num_attributes; ++index) {
+            const auto attribute{ToGenericAttribute(index)};
             for (u32 element = 0; element < 4; ++element) {
-                code.AddLine(fmt::format("case 0x{:x}: return {};", 0x80 + index * 16 + element * 4,
-                                         ReadAttribute(ToGenericAttribute(index), element)));
+                constexpr u32 generic_base{0x80};
+                constexpr u32 generic_stride{16};
+                constexpr u32 element_stride{4};
+                const u32 address{generic_base + index * generic_stride + element * element_stride};
+
+                const bool declared{stage != ShaderStage::Fragment ||
+                                    header.ps.GetAttributeUse(index) != AttributeUse::Unused};
+                const std::string value{declared ? ReadAttribute(attribute, element) : "0"};
+                code.AddLine(fmt::format("case 0x{:x}: return {};", address, value));
             }
         }
 

From d4df803b2b6bab96321ca69651e4132545b433eb Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 May 2019 21:45:53 -0300
Subject: [PATCH 11/11] shader_ir/other: Implement IPA.IDX

---
 src/video_core/engines/shader_bytecode.h |  1 +
 src/video_core/shader/decode/other.cpp   | 13 ++++++++-----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e4a9471b8..7bbc556da 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -596,6 +596,7 @@ union Instruction {
     } alu;
 
     union {
+        BitField<38, 1, u64> idx;
         BitField<51, 1, u64> saturate;
         BitField<52, 2, IpaSampleMode> sample_mode;
         BitField<54, 2, IpaInterpMode> interp_mode;
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index 776bdb931..fa17c45b5 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -130,15 +130,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
         break;
     }
     case OpCode::Id::IPA: {
-        const auto& attribute = instr.attribute.fmt28;
+        const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff;
+
+        const auto attribute = instr.attribute.fmt28;
         const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
                                                 instr.ipa.sample_mode.Value()};
 
-        const Node attr = GetInputAttribute(attribute.index, attribute.element);
-        Node value = attr;
+        Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
+                                 : GetInputAttribute(attribute.index, attribute.element);
         const Tegra::Shader::Attribute::Index index = attribute.index.Value();
-        if (index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
-            index <= Tegra::Shader::Attribute::Index::Attribute_31) {
+        const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
+                                index <= Tegra::Shader::Attribute::Index::Attribute_31;
+        if (is_generic || is_physical) {
             // TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
             // In theory by setting them as perspective, OpenGL does the perspective correction.
             // A way must figured to reverse the last step of it.