diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index ebf85f311..056ef495c 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -273,9 +273,9 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
     specialization.workgroup_size = key.workgroup_size;
     specialization.shared_memory_size = key.shared_memory_size;
 
-    const SPIRVShader spirv_shader{
-        Decompile(device, shader->GetIR(), ShaderType::Compute, specialization),
-        shader->GetEntries()};
+    const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute,
+                                             shader->GetRegistry(), specialization),
+                                   shader->GetEntries()};
     entry = std::make_unique<VKComputePipeline>(device, scheduler, descriptor_pool,
                                                 update_descriptor_queue, spirv_shader);
     return *entry;
@@ -324,8 +324,7 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
     const auto& gpu = system.GPU().Maxwell3D();
 
     Specialization specialization;
-    specialization.primitive_topology = fixed_state.input_assembly.topology;
-    if (specialization.primitive_topology == Maxwell::PrimitiveTopology::Points) {
+    if (fixed_state.input_assembly.topology == Maxwell::PrimitiveTopology::Points) {
         ASSERT(fixed_state.input_assembly.point_size != 0.0f);
         specialization.point_size = fixed_state.input_assembly.point_size;
     }
@@ -333,9 +332,6 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
         specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].type;
     }
     specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one;
-    specialization.tessellation.primitive = fixed_state.tessellation.primitive;
-    specialization.tessellation.spacing = fixed_state.tessellation.spacing;
-    specialization.tessellation.clockwise = fixed_state.tessellation.clockwise;
 
     SPIRVProgram program;
     std::vector<vk::DescriptorSetLayoutBinding> bindings;
@@ -356,8 +352,9 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
         const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5
         const auto program_type = GetShaderType(program_enum);
         const auto& entries = shader->GetEntries();
-        program[stage] = {Decompile(device, shader->GetIR(), program_type, specialization),
-                          entries};
+        program[stage] = {
+            Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization),
+            entries};
 
         if (program_enum == Maxwell::ShaderProgram::VertexA) {
             // VertexB was combined with VertexA, so we skip the VertexB iteration
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index e292526bb..21340c9a4 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -132,6 +132,10 @@ public:
         return shader_ir;
     }
 
+    const VideoCommon::Shader::Registry& GetRegistry() const {
+        return registry;
+    }
+
     const VideoCommon::Shader::ShaderIR& GetIR() const {
         return shader_ir;
     }
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index cfcca5af0..699a538d6 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -24,6 +24,7 @@
 #include "video_core/renderer_vulkan/vk_shader_decompiler.h"
 #include "video_core/shader/node.h"
 #include "video_core/shader/shader_ir.h"
+#include "video_core/shader/transform_feedback.h"
 
 namespace Vulkan {
 
@@ -266,9 +267,10 @@ bool IsPrecise(Operation operand) {
 class SPIRVDecompiler final : public Sirit::Module {
 public:
     explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderType stage,
-                             const Specialization& specialization)
+                             const Registry& registry, const Specialization& specialization)
         : Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()},
-          specialization{specialization} {
+          registry{registry}, specialization{specialization},
+          transform_feedback{BuildTransformFeedback(registry.GetGraphicsInfo())} {
         AddCapability(spv::Capability::Shader);
         AddCapability(spv::Capability::UniformAndStorageBuffer16BitAccess);
         AddCapability(spv::Capability::ImageQuery);
@@ -318,25 +320,29 @@ public:
             AddExecutionMode(main, spv::ExecutionMode::OutputVertices,
                              header.common2.threads_per_input_primitive);
             break;
-        case ShaderType::TesselationEval:
+        case ShaderType::TesselationEval: {
+            const auto& info = registry.GetGraphicsInfo();
             AddCapability(spv::Capability::Tessellation);
             AddEntryPoint(spv::ExecutionModel::TessellationEvaluation, main, "main", interfaces);
-            AddExecutionMode(main, GetExecutionMode(specialization.tessellation.primitive));
-            AddExecutionMode(main, GetExecutionMode(specialization.tessellation.spacing));
-            AddExecutionMode(main, specialization.tessellation.clockwise
+            AddExecutionMode(main, GetExecutionMode(info.tessellation_primitive));
+            AddExecutionMode(main, GetExecutionMode(info.tessellation_spacing));
+            AddExecutionMode(main, info.tessellation_clockwise
                                        ? spv::ExecutionMode::VertexOrderCw
                                        : spv::ExecutionMode::VertexOrderCcw);
             break;
-        case ShaderType::Geometry:
+        }
+        case ShaderType::Geometry: {
+            const auto& info = registry.GetGraphicsInfo();
             AddCapability(spv::Capability::Geometry);
             AddEntryPoint(spv::ExecutionModel::Geometry, main, "main", interfaces);
-            AddExecutionMode(main, GetExecutionMode(specialization.primitive_topology));
+            AddExecutionMode(main, GetExecutionMode(info.primitive_topology));
             AddExecutionMode(main, GetExecutionMode(header.common3.output_topology));
             AddExecutionMode(main, spv::ExecutionMode::OutputVertices,
                              header.common4.max_output_vertices);
             // TODO(Rodrigo): Where can we get this info from?
             AddExecutionMode(main, spv::ExecutionMode::Invocations, 1U);
             break;
+        }
         case ShaderType::Fragment:
             AddEntryPoint(spv::ExecutionModel::Fragment, main, "main", interfaces);
             AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
@@ -545,7 +551,8 @@ private:
         if (stage != ShaderType::Geometry) {
             return;
         }
-        const u32 num_input = GetNumPrimitiveTopologyVertices(specialization.primitive_topology);
+        const auto& info = registry.GetGraphicsInfo();
+        const u32 num_input = GetNumPrimitiveTopologyVertices(info.primitive_topology);
         DeclareInputVertexArray(num_input);
         DeclareOutputVertex();
     }
@@ -898,7 +905,7 @@ private:
     u32 GetNumInputVertices() const {
         switch (stage) {
         case ShaderType::Geometry:
-            return GetNumPrimitiveTopologyVertices(specialization.primitive_topology);
+            return GetNumPrimitiveTopologyVertices(registry.GetGraphicsInfo().primitive_topology);
         case ShaderType::TesselationControl:
         case ShaderType::TesselationEval:
             return NumInputPatches;
@@ -2495,7 +2502,9 @@ private:
     const ShaderIR& ir;
     const ShaderType stage;
     const Tegra::Shader::Header header;
+    const Registry& registry;
     const Specialization& specialization;
+    const std::unordered_map<u8, VaryingTFB> transform_feedback;
 
     const Id t_void = Name(TypeVoid(), "void");
 
@@ -2870,8 +2879,9 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) {
 }
 
 std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
-                           ShaderType stage, const Specialization& specialization) {
-    return SPIRVDecompiler(device, ir, stage, specialization).Assemble();
+                           ShaderType stage, const VideoCommon::Shader::Registry& registry,
+                           const Specialization& specialization) {
+    return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble();
 }
 
 } // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
index f5dc14d9e..ffea4709e 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
@@ -15,6 +15,7 @@
 #include "common/common_types.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/engines/shader_type.h"
+#include "video_core/shader/registry.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace Vulkan {
@@ -91,17 +92,9 @@ struct Specialization final {
     u32 shared_memory_size{};
 
     // Graphics specific
-    Maxwell::PrimitiveTopology primitive_topology{};
     std::optional<float> point_size{};
     std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
     bool ndc_minus_one_to_one{};
-
-    // Tessellation specific
-    struct {
-        Maxwell::TessellationPrimitive primitive{};
-        Maxwell::TessellationSpacing spacing{};
-        bool clockwise{};
-    } tessellation;
 };
 // Old gcc versions don't consider this trivially copyable.
 // static_assert(std::is_trivially_copyable_v<Specialization>);
@@ -114,6 +107,8 @@ struct SPIRVShader {
 ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir);
 
 std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
-                           Tegra::Engines::ShaderType stage, const Specialization& specialization);
+                           Tegra::Engines::ShaderType stage,
+                           const VideoCommon::Shader::Registry& registry,
+                           const Specialization& specialization);
 
 } // namespace Vulkan