diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8a9fdc3be..301024cf8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -39,9 +39,9 @@
     <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
     <PackageVersion Include="shaderc.net" Version="0.1.0" />
     <PackageVersion Include="SharpZipLib" Version="1.4.2" />
-    <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
-    <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
-    <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
+    <PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
+    <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
+    <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
     <PackageVersion Include="SkiaSharp" Version="2.88.7" />
     <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
     <PackageVersion Include="SPB" Version="0.0.4-build32" />
@@ -49,4 +49,4 @@
     <PackageVersion Include="System.Management" Version="8.0.0" />
     <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
   </ItemGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
index 7fe2a4f02..a9163f348 100644
--- a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
+++ b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
@@ -1,13 +1,33 @@
+using Ryujinx.Common.Utilities;
 using System;
 
 namespace Ryujinx.Common.GraphicsDriver
 {
     public static class DriverUtilities
     {
+        private static void AddMesaFlags(string envVar, string newFlags)
+        {
+            string existingFlags = Environment.GetEnvironmentVariable(envVar);
+
+            string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
+
+            OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
+        }
+
+        public static void InitDriverConfig(bool oglThreading)
+        {
+            if (OperatingSystem.IsLinux())
+            {
+                AddMesaFlags("RADV_DEBUG", "nodcc");
+            }
+
+            ToggleOGLThreading(oglThreading);
+        }
+
         public static void ToggleOGLThreading(bool enabled)
         {
-            Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
-            Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
+            OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
+            OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
 
             try
             {
diff --git a/src/Ryujinx.Common/Utilities/OsUtils.cs b/src/Ryujinx.Common/Utilities/OsUtils.cs
new file mode 100644
index 000000000..a0791b092
--- /dev/null
+++ b/src/Ryujinx.Common/Utilities/OsUtils.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common.Utilities
+{
+    public partial class OsUtils
+    {
+        [LibraryImport("libc", SetLastError = true)]
+        private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
+
+        public static void SetEnvironmentVariableNoCaching(string key, string value)
+        {
+            // Set the value in the cached environment variables, too.
+            Environment.SetEnvironmentVariable(key, value);
+
+            if (!OperatingSystem.IsWindows())
+            {
+                int res = setenv(key, value, 1);
+                Debug.Assert(res != -1);
+            }
+        }
+    }
+}
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
index dd55e7d1d..35051c6e0 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -415,7 +415,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
 #pragma warning disable CS0649 // Field is never assigned to
         public int Width;
         public int Height;
-        public int Depth;
+        public ushort Depth;
+        public ushort Flags;
+
+        public readonly bool UnpackIsLayered()
+        {
+            return (Flags & 1) == 0;
+        }
 #pragma warning restore CS0649
     }
 
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index b9ff060e2..b6fa842e3 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -468,13 +468,11 @@ namespace Ryujinx.Graphics.Gpu.Image
             int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
             int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
 
+            layered &= size.UnpackIsLayered();
+
             Target target;
 
-            if (dsState.MemoryLayout.UnpackIsTarget3D())
-            {
-                target = Target.Texture3D;
-            }
-            else if ((samplesInX | samplesInY) != 1)
+            if ((samplesInX | samplesInY) != 1)
             {
                 target = size.Depth > 1 && layered
                     ? Target.Texture2DMultisampleArray
diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
index a6a006bb9..bcfb3dbfe 100644
--- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
             CommandBuffer
         }
 
+        private bool _feedbackLoopActive;
         private PipelineStageFlags _incoherentBufferWriteStages;
         private PipelineStageFlags _incoherentTextureWriteStages;
         private PipelineStageFlags _extraStages;
         private IncoherentBarrierType _queuedIncoherentBarrier;
+        private bool _queuedFeedbackLoopBarrier;
 
         public BarrierBatch(VulkanRenderer gd)
         {
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
                 stages |= PipelineStageFlags.TransformFeedbackBitExt;
             }
 
-            if (!gd.IsTBDR)
-            {
-                // Desktop GPUs can transform image barriers into memory barriers.
-
-                access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
-                access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
-
-                stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
-                stages |= PipelineStageFlags.ColorAttachmentOutputBit;
-            }
-
             return (access, stages);
         }
 
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
                     }
 
                     _queuedIncoherentBarrier = IncoherentBarrierType.None;
+                    _queuedFeedbackLoopBarrier = false;
                 }
+                else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
+                {
+                    // Feedback loop barrier.
+
+                    MemoryBarrier barrier = new MemoryBarrier()
+                    {
+                        SType = StructureType.MemoryBarrier,
+                        SrcAccessMask = AccessFlags.ShaderWriteBit,
+                        DstAccessMask = AccessFlags.ShaderReadBit
+                    };
+
+                    QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
+
+                    _queuedFeedbackLoopBarrier = false;
+                }
+
+                _feedbackLoopActive = false;
             }
         }
 
         public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
         {
-            Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
+            Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
         }
 
-        public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
+        public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
         {
             if (program != null)
             {
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
                 _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
             }
 
+            _feedbackLoopActive |= feedbackLoopActive;
+
             FlushMemoryBarrier(program, inRenderPass);
 
             if (!inRenderPass && rpHolder != null)
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 _queuedIncoherentBarrier = type;
             }
+
+            _queuedFeedbackLoopBarrier = true;
         }
 
         public void QueueTextureBarrier()
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
index 3dcbc3130..e840fdc02 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Vulkan
                 Range = (uint)size,
             };
 
-            _gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
+            _gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
 
             return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer);
         }
@@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PipelineStageFlags.AllCommandsBit,
                     DependencyFlags.DeviceGroupBit,
                     1,
-                    memoryBarrier,
+                    in memoryBarrier,
                     0,
                     null,
                     0,
@@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Vulkan
                 0,
                 null,
                 1,
-                memoryBarrier,
+                in memoryBarrier,
                 0,
                 null);
         }
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 1b6ac9988..7523913ec 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PBufferBinds = &bufferBind
                 };
 
-                gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
+                gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError();
             }
 
             var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs
index d585dd53c..e49df765d 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferState.cs
@@ -25,7 +25,10 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 var buffer = _buffer.Get(cbs, _offset, _size, true).Value;
 
-                gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size);
+                ulong offset = (ulong)_offset;
+                ulong size = (ulong)_size;
+
+                gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size);
             }
         }
 
diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
index e3938392f..e1fd3fb9d 100644
--- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
+++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan
                     Level = CommandBufferLevel.Primary,
                 };
 
-                api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
+                api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer);
 
                 Dependants = new List<IAuto>();
                 Waitables = new List<MultiFenceHolder>();
@@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
                         CommandPoolCreateFlags.ResetCommandBufferBit,
             };
 
-            api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
+            api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError();
 
             // We need at least 2 command buffers to get texture data in some cases.
             _totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
@@ -253,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan
                             SType = StructureType.CommandBufferBeginInfo,
                         };
 
-                        _api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError();
+                        _api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError();
 
                         return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
                     }
@@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Vulkan
 
                         lock (_queueLock)
                         {
-                            _api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
+                            _api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
                         }
                     }
                 }
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
index 846dd5c7d..40fc01b24 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
@@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PBufferInfo = &bufferInfo,
                 };
 
-                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
             }
         }
 
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PBufferInfo = pBufferInfo,
                 };
 
-                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
             }
         }
 
@@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PImageInfo = &imageInfo,
                 };
 
-                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
             }
         }
 
@@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PImageInfo = pImageInfo,
                 };
 
-                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
             }
         }
 
@@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan
                             PImageInfo = pImageInfo,
                         };
 
-                        _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                        _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
 
                         i += count - 1;
                     }
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PTexelBufferView = &texelBufferView,
                 };
 
-                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
             }
         }
 
@@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan
                             PTexelBufferView = pTexelBufferView + i,
                         };
 
-                        _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+                        _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
                     }
 
                     i += count;
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
index 707ae1292..97669942c 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
                         PPoolSizes = pPoolsSize,
                     };
 
-                    Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
+                    Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
                 }
             }
 
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index 563fdafd3..298526d51 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
 using Silk.NET.Vulkan;
 using System;
 using System.Buffers;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
@@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
         private record struct TextureRef
         {
             public ShaderStage Stage;
-            public TextureStorage Storage;
-            public Auto<DisposableImageView> View;
+            public TextureView View;
+            public Auto<DisposableImageView> ImageView;
             public Auto<DisposableSampler> Sampler;
 
-            public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
+            public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
             {
                 Stage = stage;
-                Storage = storage;
                 View = view;
+                ImageView = imageView;
                 Sampler = sampler;
             }
         }
@@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
         private record struct ImageRef
         {
             public ShaderStage Stage;
-            public TextureStorage Storage;
-            public Auto<DisposableImageView> View;
+            public TextureView View;
+            public Auto<DisposableImageView> ImageView;
 
-            public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
+            public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
             {
                 Stage = stage;
-                Storage = storage;
                 View = view;
+                ImageView = imageView;
             }
         }
 
@@ -124,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly TextureView _dummyTexture;
         private readonly SamplerHolder _dummySampler;
 
+        public List<TextureView> FeedbackLoopHazards { get; private set; }
+
         public DescriptorSetUpdater(VulkanRenderer gd, Device device)
         {
             _gd = gd;
@@ -209,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan
             _templateUpdater = new();
         }
 
-        public void Initialize()
+        public void Initialize(bool isMainPipeline)
         {
             MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
             _dummyTexture.SetData(dummyTextureData);
+
+            if (isMainPipeline)
+            {
+                FeedbackLoopHazards = new();
+            }
         }
 
         private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
@@ -275,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan
 
         public void InsertBindingBarriers(CommandBufferScoped cbs)
         {
+            if ((FeedbackLoopHazards?.Count ?? 0) > 0)
+            {
+                // Clear existing hazards - they will be rebuilt.
+
+                foreach (TextureView hazard in FeedbackLoopHazards)
+                {
+                    hazard.DecrementHazardUses();
+                }
+
+                FeedbackLoopHazards.Clear();
+            }
+
             foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
             {
                 if (segment.Type == ResourceType.TextureAndSampler)
@@ -284,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
                         for (int i = 0; i < segment.Count; i++)
                         {
                             ref var texture = ref _textureRefs[segment.Binding + i];
-                            texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
+                            texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
                         }
                     }
                     else
@@ -305,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan
                         for (int i = 0; i < segment.Count; i++)
                         {
                             ref var image = ref _imageRefs[segment.Binding + i];
-                            image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
+                            image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
                         }
                     }
                     else
@@ -385,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan
             }
             else if (image is TextureView view)
             {
-                view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+                ref ImageRef iRef = ref _imageRefs[binding];
 
-                _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
+                iRef.View?.ClearUsage(FeedbackLoopHazards);
+                view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
+
+                iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
             }
             else
             {
@@ -486,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan
             }
             else if (texture is TextureView view)
             {
-                view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+                ref TextureRef iRef = ref _textureRefs[binding];
 
-                _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
+                iRef.View?.ClearUsage(FeedbackLoopHazards);
+                view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
+
+                iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
             }
             else
             {
@@ -510,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
 
-                _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
+                _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
 
                 SignalDirty(DirtyFlags.Texture);
             }
@@ -836,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan
                                 ref var texture = ref textures[i];
                                 ref var refs = ref _textureRefs[binding + i];
 
-                                texture.ImageView = refs.View?.Get(cbs).Value ?? default;
+                                texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
                                 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
 
                                 if (texture.ImageView.Handle == 0)
@@ -886,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan
 
                             for (int i = 0; i < count; i++)
                             {
-                                images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
+                                images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
                             }
 
                             tu.Push<DescriptorImageInfo>(images[..count]);
@@ -957,7 +983,7 @@ namespace Ryujinx.Graphics.Vulkan
                             ref var texture = ref textures[i];
                             ref var refs = ref _textureRefs[binding + i];
 
-                            texture.ImageView = refs.View?.Get(cbs).Value ?? default;
+                            texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
                             texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
 
                             if (texture.ImageView.Handle == 0)
diff --git a/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
new file mode 100644
index 000000000..22f73679d
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+    [Flags]
+    internal enum FeedbackLoopAspects
+    {
+        None = 0,
+        Color = 1 << 0,
+        Depth = 1 << 1,
+    }
+}
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index 5c5a8f3ad..8d80e9d05 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Vulkan
                 Layers = Layers,
             };
 
-            api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
+            api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
             return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
         }
 
@@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
             _depthStencil?.Storage?.AddStoreOpUsage(true);
         }
 
+        public void ClearBindings()
+        {
+            _depthStencil?.Storage.ClearBindings();
+
+            for (int i = 0; i < _colorsCanonical.Length; i++)
+            {
+                _colorsCanonical[i]?.Storage.ClearBindings();
+            }
+        }
+
+        public void AddBindings()
+        {
+            _depthStencil?.Storage.AddBinding(_depthStencil);
+
+            for (int i = 0; i < _colorsCanonical.Length; i++)
+            {
+                TextureView color = _colorsCanonical[i];
+                color?.Storage.AddBinding(color);
+            }
+        }
+
         public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
             VulkanRenderer gd,
             Device device,
diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
index b6694bcb3..bd17867b1 100644
--- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
         public readonly bool SupportsViewportArray2;
         public readonly bool SupportsHostImportedMemory;
         public readonly bool SupportsDepthClipControl;
+        public readonly bool SupportsAttachmentFeedbackLoop;
+        public readonly bool SupportsDynamicAttachmentFeedbackLoop;
         public readonly uint SubgroupSize;
         public readonly SampleCountFlags SupportedSampleCounts;
         public readonly PortabilitySubsetFlags PortabilitySubset;
@@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
             bool supportsViewportArray2,
             bool supportsHostImportedMemory,
             bool supportsDepthClipControl,
+            bool supportsAttachmentFeedbackLoop,
+            bool supportsDynamicAttachmentFeedbackLoop,
             uint subgroupSize,
             SampleCountFlags supportedSampleCounts,
             PortabilitySubsetFlags portabilitySubset,
@@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
             SupportsViewportArray2 = supportsViewportArray2;
             SupportsHostImportedMemory = supportsHostImportedMemory;
             SupportsDepthClipControl = supportsDepthClipControl;
+            SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
+            SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
             SubgroupSize = subgroupSize;
             SupportedSampleCounts = supportedSampleCounts;
             PortabilitySubset = portabilitySubset;
diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs
index baccc698f..ff1565246 100644
--- a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs
@@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PNext = &importInfo,
                 };
 
-                Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory);
+                Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory);
 
                 if (result < Result.Success)
                 {
diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
index a1acc90f9..3d42ed7e2 100644
--- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
+++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
                 MemoryTypeIndex = (uint)MemoryTypeIndex,
             };
 
-            _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
+            _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
 
             IntPtr hostPointer = IntPtr.Zero;
 
diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
index 457240aa0..930d6b525 100644
--- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
+++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
@@ -1,3 +1,4 @@
+using Silk.NET.Core.Loader;
 using Silk.NET.Vulkan;
 using System;
 using System.Runtime.InteropServices;
@@ -8,6 +9,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
     [SupportedOSPlatform("macos")]
     public static partial class MVKInitialization
     {
+        private const string VulkanLib = "libvulkan.dylib";
+
         [LibraryImport("libMoltenVK.dylib")]
         private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize);
 
@@ -29,5 +32,20 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
 
             vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
         }
+
+        private static string[] Resolver(string path)
+        {
+            if (path.EndsWith(VulkanLib))
+            {
+                path = path[..^VulkanLib.Length] + "libMoltenVK.dylib";
+                return [path];
+            }
+            return Array.Empty<string>();
+        }
+
+        public static void InitializeResolver()
+        {
+            ((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver);
+        }
     }
 }
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index bda6167d7..20c4b2572 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Shader;
 using Silk.NET.Vulkan;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Numerics;
 using System.Runtime.CompilerServices;
@@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
         public readonly Action EndRenderPassDelegate;
 
         protected PipelineDynamicState DynamicState;
+        protected bool IsMainPipeline;
         private PipelineState _newState;
         private bool _graphicsStateDirty;
         private bool _computeStateDirty;
@@ -85,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
         private bool _tfEnabled;
         private bool _tfActive;
 
+        private FeedbackLoopAspects _feedbackLoop;
+        private bool _passWritesDepthStencil;
+
         private readonly PipelineColorBlendAttachmentState[] _storedBlend;
         public ulong DrawCount { get; private set; }
         public bool RenderPassActive { get; private set; }
@@ -102,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
                 SType = StructureType.PipelineCacheCreateInfo,
             };
 
-            gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
+            gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
 
             _descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
             _vertexBufferUpdater = new VertexBufferUpdater(gd);
@@ -126,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
 
         public void Initialize()
         {
-            _descriptorSetUpdater.Initialize();
+            _descriptorSetUpdater.Initialize(IsMainPipeline);
 
             QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
             TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
@@ -814,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan
             _newState.DepthTestEnable = depthTest.TestEnable;
             _newState.DepthWriteEnable = depthTest.WriteEnable;
             _newState.DepthCompareOp = depthTest.Func.Convert();
+
+            UpdatePassDepthStencil();
             SignalStateChange();
         }
 
@@ -1079,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan
             _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
             _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
             _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
+
+            UpdatePassDepthStencil();
             SignalStateChange();
         }
 
@@ -1426,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
                 }
             }
 
+            if (IsMainPipeline)
+            {
+                FramebufferParams?.ClearBindings();
+            }
+
             FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
+
+            if (IsMainPipeline)
+            {
+                FramebufferParams.AddBindings();
+
+                _newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
+                _bindingBarriersDirty = true;
+            }
+
+            _passWritesDepthStencil = false;
+            UpdatePassDepthStencil();
             UpdatePipelineAttachmentFormats();
         }
 
@@ -1493,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan
                 }
             }
 
-            Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
+            Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
 
             _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
         }
 
+        private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
+        {
+            if (_feedbackLoop != aspects)
+            {
+                if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
+                {
+                    DynamicState.SetFeedbackLoop(aspects);
+                }
+                else
+                {
+                    _newState.FeedbackLoopAspects = aspects;
+                }
+
+                _feedbackLoop = aspects;
+
+                return true;
+            }
+
+            return false;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool UpdateFeedbackLoop()
+        {
+            List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards;
+
+            if ((hazards?.Count ?? 0) > 0)
+            {
+                FeedbackLoopAspects aspects = 0;
+
+                foreach (TextureView view in hazards)
+                {
+                    // May need to enforce feedback loop layout here in the future.
+                    // Though technically, it should always work with the general layout.
+
+                    if (view.Info.Format.IsDepthOrStencil())
+                    {
+                        if (_passWritesDepthStencil)
+                        {
+                            // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
+
+                            aspects |= FeedbackLoopAspects.Depth;
+                        }
+                    }
+                    else
+                    {
+                        aspects |= FeedbackLoopAspects.Color;
+                    }
+                }
+
+                return ChangeFeedbackLoop(aspects);
+            }
+            else if (_feedbackLoop != 0)
+            {
+                return ChangeFeedbackLoop(FeedbackLoopAspects.None);
+            }
+
+            return false;
+        }
+
+        private void UpdatePassDepthStencil()
+        {
+            if (!RenderPassActive)
+            {
+                _passWritesDepthStencil = false;
+            }
+
+            // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
+            _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
+        }
+
         private bool RecreateGraphicsPipelineIfNeeded()
         {
             if (AutoFlush.ShouldFlushDraw(DrawCount))
@@ -1505,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan
                 Gd.FlushAllCommands();
             }
 
-            DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
+            DynamicState.ReplayIfDirty(Gd, CommandBuffer);
 
             if (_needsIndexBufferRebind && _indexBufferPattern == null)
             {
@@ -1539,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan
                 _vertexBufferUpdater.Commit(Cbs);
             }
 
-            if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
+            if (_bindingBarriersDirty)
+            {
+                // Stale barriers may have been activated by switching program. Emit any that are relevant.
+                _descriptorSetUpdater.InsertBindingBarriers(Cbs);
+
+                _bindingBarriersDirty = false;
+            }
+
+            if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
             {
                 if (!CreatePipeline(PipelineBindPoint.Graphics))
                 {
@@ -1548,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan
 
                 _graphicsStateDirty = false;
                 Pbp = PipelineBindPoint.Graphics;
-
-                if (_bindingBarriersDirty)
-                {
-                    // Stale barriers may have been activated by switching program. Emit any that are relevant.
-                    _descriptorSetUpdater.InsertBindingBarriers(Cbs);
-
-                    _bindingBarriersDirty = false;
-                }
             }
 
-            Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
+            Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
 
             _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
 
@@ -1628,7 +1724,7 @@ namespace Ryujinx.Graphics.Vulkan
                     ClearValueCount = 1,
                 };
 
-                Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
+                Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
                 RenderPassActive = true;
             }
         }
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
index 89ce10b0a..85069c6b2 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
@@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan
                     DependencyCount = 1,
                 };
 
-                gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+                gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
 
                 return new DisposableRenderPass(gd.Api, device, renderPass);
             }
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
index 1cc33f728..ad26ff7b3 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Common.Memory;
 using Silk.NET.Vulkan;
+using Silk.NET.Vulkan.Extensions.EXT;
 
 namespace Ryujinx.Graphics.Vulkan
 {
@@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
 
         private Array4<float> _blendConstants;
 
+        private FeedbackLoopAspects _feedbackLoopAspects;
+
         public uint ViewportsCount;
         public Array16<Viewport> Viewports;
 
@@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
             Scissor = 1 << 2,
             Stencil = 1 << 3,
             Viewport = 1 << 4,
-            All = Blend | DepthBias | Scissor | Stencil | Viewport,
+            FeedbackLoop = 1 << 5,
+            All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
         }
 
         private DirtyFlags _dirty;
@@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
+        public void SetFeedbackLoop(FeedbackLoopAspects aspects)
+        {
+            _feedbackLoopAspects = aspects;
+
+            _dirty |= DirtyFlags.FeedbackLoop;
+        }
+
         public void ForceAllDirty()
         {
             _dirty = DirtyFlags.All;
         }
 
-        public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer)
+        public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
         {
+            Vk api = gd.Api;
+
             if (_dirty.HasFlag(DirtyFlags.Blend))
             {
                 RecordBlend(api, commandBuffer);
@@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
                 RecordViewport(api, commandBuffer);
             }
 
+            if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
+            {
+                RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
+            }
+
             _dirty = DirtyFlags.None;
         }
 
@@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
                 api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
             }
         }
+
+        private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
+        {
+            ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
+
+            if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
+            {
+                aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
+            }
+
+            api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
+        }
     }
 }
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index cf65eefb0..54d43bdba 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
             _activeBufferMirrors = new();
 
             CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
+
+            IsMainPipeline = true;
         }
 
         private void CopyPendingQuery()
@@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
             {
-                DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
+                DynamicState.ReplayIfDirty(Gd, CommandBuffer);
             }
         }
 
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
index bca119f6a..8d7815616 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
                         Flags = flags,
                     };
 
-                    gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
+                    gd.Api.CreateDescriptorSetLayout(device, in descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
                 }
             }
 
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
index 6b6b46a91..a726b9edb 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
     struct PipelineState : IDisposable
     {
         private const int RequiredSubgroupSize = 32;
+        private const int MaxDynamicStatesCount = 9;
 
         public PipelineUid Internal;
 
@@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
             set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
         }
 
+        public FeedbackLoopAspects FeedbackLoopAspects
+        {
+            readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
+            set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
+        }
+
         public bool HasTessellationControlShader;
         public NativeArray<PipelineShaderStageCreateInfo> Stages;
         public PipelineLayout PipelineLayout;
@@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
                 }
 
                 bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
-                int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
+                bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
 
-                DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
+                DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
+
+                int dynamicStatesCount = 7;
 
                 dynamicStates[0] = DynamicState.Viewport;
                 dynamicStates[1] = DynamicState.Scissor;
@@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
 
                 if (supportsExtDynamicState)
                 {
-                    dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
+                    dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
+                }
+
+                if (supportsFeedbackLoopDynamicState)
+                {
+                    dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
                 }
 
                 var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
@@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
                     PDynamicStates = dynamicStates,
                 };
 
+                PipelineCreateFlags flags = 0;
+
+                if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
+                {
+                    FeedbackLoopAspects aspects = FeedbackLoopAspects;
+
+                    if ((aspects & FeedbackLoopAspects.Color) != 0)
+                    {
+                        flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
+                    }
+
+                    if ((aspects & FeedbackLoopAspects.Depth) != 0)
+                    {
+                        flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
+                    }
+                }
+
                 var pipelineCreateInfo = new GraphicsPipelineCreateInfo
                 {
                     SType = StructureType.GraphicsPipelineCreateInfo,
+                    Flags = flags,
                     StageCount = StagesCount,
                     PStages = Stages.Pointer,
                     PVertexInputState = &vertexInputState,
diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs
index 714cb2833..c9a546648 100644
--- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs
@@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
                     PipelineStatistics = flags,
                 };
 
-                gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
+                gd.Api.CreateQueryPool(device, in queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
             }
 
             var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true);
diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
index b2dd0dd87..a364c5716 100644
--- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
                     DependencyCount = 1,
                 };
 
-                gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+                gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
 
                 _renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
             }
diff --git a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs
index f67daeecc..7f37ab139 100644
--- a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs
@@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
                 samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
             }
 
-            gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
+            gd.Api.CreateSampler(device, in samplerCreateInfo, null, out var sampler).ThrowOnError();
 
             _sampler = new Auto<DisposableSampler>(new DisposableSampler(gd.Api, device, sampler));
         }
diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs
index 06f3499db..1c8caffd9 100644
--- a/src/Ryujinx.Graphics.Vulkan/Shader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs
@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
                         PCode = (uint*)pCode,
                     };
 
-                    api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
+                    api.CreateShaderModule(device, in shaderModuleCreateInfo, null, out _module).ThrowOnError();
                 }
 
                 CompileStatus = ProgramLinkStatus.Success;
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
index fdc0a248b..45cddd772 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan
                     DstOffsets = dstOffsets,
                 };
 
-                api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter);
+                api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter);
 
                 copySrcLevel++;
                 copyDstLevel++;
@@ -320,13 +320,13 @@ namespace Ryujinx.Graphics.Vulkan
                 {
                     var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
 
-                    api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
+                    api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
                 }
                 else
                 {
                     var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
 
-                    api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
+                    api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
                 }
 
                 width = Math.Max(1, width >> 1);
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
                     DependencyCount = 1,
                 };
 
-                gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+                gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
 
                 using var rp = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
 
@@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Vulkan
                     Layers = (uint)src.Layers,
                 };
 
-                gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
+                gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
                 using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView);
 
                 var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height));
@@ -465,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
                 // to resolve the depth-stencil texture.
                 // TODO: Do speculative resolve and part of the same render pass as the draw to avoid
                 // ending the current render pass?
-                gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
+                gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
                 gd.Api.CmdEndRenderPass(cbs.CommandBuffer);
             }
         }
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index f36db68de..10b36a3f9 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
 using System;
 using System.Collections.Generic;
 using System.Numerics;
+using System.Runtime.CompilerServices;
 using Format = Ryujinx.Graphics.GAL.Format;
 using VkBuffer = Silk.NET.Vulkan.Buffer;
 using VkFormat = Silk.NET.Vulkan.Format;
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
 {
     class TextureStorage : IDisposable
     {
+        private struct TextureSliceInfo
+        {
+            public int BindCount;
+        }
+
         private const MemoryPropertyFlags DefaultImageMemoryFlags =
             MemoryPropertyFlags.DeviceLocalBit;
 
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly Image _image;
         private readonly Auto<DisposableImage> _imageAuto;
         private readonly Auto<MemoryAllocation> _allocationAuto;
+        private readonly int _depthOrLayers;
         private Auto<MemoryAllocation> _foreignAllocationAuto;
 
         private Dictionary<Format, TextureStorage> _aliasedStorages;
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
         private int _viewsCount;
         private readonly ulong _size;
 
+        private int _bindCount;
+        private readonly TextureSliceInfo[] _slices;
+
         public VkFormat VkFormat { get; }
 
         public unsafe TextureStorage(
@@ -73,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
             var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
 
             VkFormat = format;
+            _depthOrLayers = info.GetDepthOrLayers();
 
             var type = info.Target.Convert();
 
@@ -80,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
 
-            var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
+            var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
 
             var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
 
@@ -114,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
                 Flags = flags,
             };
 
-            gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError();
+            gd.Api.CreateImage(device, in imageCreateInfo, null, out _image).ThrowOnError();
 
             if (foreignAllocation == null)
             {
@@ -148,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
 
                 InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
             }
+
+            _slices = new TextureSliceInfo[levels * _depthOrLayers];
         }
 
         public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
@@ -284,7 +297,7 @@ namespace Ryujinx.Graphics.Vulkan
                 0,
                 null,
                 1,
-                barrier);
+                in barrier);
 
             if (useTempCbs)
             {
@@ -292,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
-        public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage)
+        public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
         {
             var usage = DefaultUsageFlags;
 
@@ -305,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan
                 usage |= ImageUsageFlags.ColorAttachmentBit;
             }
 
+            bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
+
             if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
             {
                 usage |= ImageUsageFlags.StorageBit;
             }
 
+            if (capabilities.SupportsAttachmentFeedbackLoop &&
+                (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
+            {
+                usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
+            }
+
             return usage;
         }
 
@@ -401,11 +422,11 @@ namespace Ryujinx.Graphics.Vulkan
 
                 if (to)
                 {
-                    _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
+                    _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
                 }
                 else
                 {
-                    _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
+                    _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
                 }
 
                 offset += mipSize;
@@ -510,6 +531,55 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
+        public void AddBinding(TextureView view)
+        {
+            // Assumes a view only has a first level.
+
+            int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
+            int layers = view.Layers;
+
+            for (int i = 0; i < layers; i++)
+            {
+                ref TextureSliceInfo info = ref _slices[index++];
+
+                info.BindCount++;
+            }
+
+            _bindCount++;
+        }
+
+        public void ClearBindings()
+        {
+            if (_bindCount != 0)
+            {
+                Array.Clear(_slices, 0, _slices.Length);
+
+                _bindCount = 0;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsBound(TextureView view)
+        {
+            if (_bindCount != 0)
+            {
+                int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
+                int layers = view.Layers;
+
+                for (int i = 0; i < layers; i++)
+                {
+                    ref TextureSliceInfo info = ref _slices[index++];
+
+                    if (info.BindCount != 0)
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
         public void IncrementViewsCount()
         {
             _viewsCount++;
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index d4f26a2dd..9b3f46662 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly Auto<DisposableImageView> _imageView2dArray;
         private Dictionary<Format, TextureView> _selfManagedViews;
 
+        private int _hazardUses;
+
         private readonly TextureCreateInfo _info;
 
         private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
@@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
             gd.Textures.Add(this);
 
             var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
-            var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
+            var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
             var levels = (uint)info.Levels;
             var layers = (uint)info.GetLayers();
 
@@ -117,7 +119,7 @@ namespace Ryujinx.Graphics.Vulkan
                     PNext = &imageViewUsage,
                 };
 
-                gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
+                gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError();
                 return new Auto<DisposableImageView>(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage());
             }
 
@@ -492,7 +494,7 @@ namespace Ryujinx.Graphics.Vulkan
                 dstStageMask,
                 DependencyFlags.None,
                 1,
-                memoryBarrier,
+                in memoryBarrier,
                 0,
                 null,
                 0,
@@ -557,7 +559,7 @@ namespace Ryujinx.Graphics.Vulkan
                 0,
                 null,
                 1,
-                memoryBarrier);
+                in memoryBarrier);
         }
 
         public TextureView GetView(Format format)
@@ -949,11 +951,11 @@ namespace Ryujinx.Graphics.Vulkan
 
                 if (to)
                 {
-                    _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
+                    _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
                 }
                 else
                 {
-                    _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
+                    _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
                 }
 
                 offset += mipSize;
@@ -1010,11 +1012,11 @@ namespace Ryujinx.Graphics.Vulkan
 
             if (to)
             {
-                _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
+                _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
             }
             else
             {
-                _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
+                _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
             }
         }
 
@@ -1034,6 +1036,34 @@ namespace Ryujinx.Graphics.Vulkan
             throw new NotImplementedException();
         }
 
+        public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
+        {
+            Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
+
+            if (feedbackLoopHazards != null && Storage.IsBound(this))
+            {
+                feedbackLoopHazards.Add(this);
+                _hazardUses++;
+            }
+        }
+
+        public void ClearUsage(List<TextureView> feedbackLoopHazards)
+        {
+            if (_hazardUses != 0 && feedbackLoopHazards != null)
+            {
+                feedbackLoopHazards.Remove(this);
+                _hazardUses--;
+            }
+        }
+
+        public void DecrementHazardUses()
+        {
+            if (_hazardUses != 0)
+            {
+                _hazardUses--;
+            }
+        }
+
         public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
             VulkanRenderer gd,
             Device device,
diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs
index 802771ede..55ae0cd81 100644
--- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs
@@ -90,11 +90,9 @@ namespace Ryujinx.Graphics.Vulkan
                 DriverId.SamsungProprietary => "Samsung",
                 DriverId.MesaVenus => "Venus",
                 DriverId.MesaDozen => "Dozen",
-
-                // TODO: Use real enum when we have an up to date Silk.NET.
-                (DriverId)24 => "NVK",
-                (DriverId)25 => "Imagination (Open)",
-                (DriverId)26 => "Honeykrisp",
+                DriverId.MesaNvk => "NVK",
+                DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
+                DriverId.MesaAgxv => "Honeykrisp",
                 _ => id.ToString(),
             };
         }
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index 5a9844cb9..2c327fdb7 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Vulkan
             "VK_EXT_4444_formats",
             "VK_KHR_8bit_storage",
             "VK_KHR_maintenance2",
+            "VK_EXT_attachment_feedback_loop_layout",
+            "VK_EXT_attachment_feedback_loop_dynamic_state",
         };
 
         private static readonly string[] _requiredExtensions = {
@@ -357,6 +359,28 @@ namespace Ryujinx.Graphics.Vulkan
                 features2.PNext = &supportedFeaturesDepthClipControl;
             }
 
+            PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
+            {
+                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
+                PNext = features2.PNext,
+            };
+
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
+            {
+                features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
+            }
+
+            PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
+            {
+                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
+                PNext = features2.PNext,
+            };
+
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
+            {
+                features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
+            }
+
             PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
             {
                 SType = StructureType.PhysicalDeviceVulkan12Features,
@@ -531,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan
                 pExtendedFeatures = &featuresDepthClipControl;
             }
 
+            PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
+
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
+                supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
+            {
+                featuresAttachmentFeedbackLoopLayout = new()
+                {
+                    SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
+                    PNext = pExtendedFeatures,
+                    AttachmentFeedbackLoopLayout = true,
+                };
+
+                pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
+            }
+
+            PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
+
+            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
+                supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
+            {
+                featuresDynamicAttachmentFeedbackLoopLayout = new()
+                {
+                    SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
+                    PNext = pExtendedFeatures,
+                    AttachmentFeedbackLoopDynamicState = true,
+                };
+
+                pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
+            }
+
             var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
 
             IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index c9ce678b7..33e41ab48 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
         internal KhrPushDescriptor PushDescriptorApi { get; private set; }
         internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
         internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
+        internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
 
         internal uint QueueFamilyIndex { get; private set; }
         internal Queue Queue { get; private set; }
@@ -149,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan
                 DrawIndirectCountApi = drawIndirectCountApi;
             }
 
+            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
+            {
+                DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
+            }
+
             if (maxQueueCount >= 2)
             {
                 Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
@@ -243,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan
                 SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
             };
 
+            PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
+            {
+                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
+            };
+
+            PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
+            {
+                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
+            };
+
             PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
             {
                 SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
@@ -279,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
                 features2.PNext = &featuresDepthClipControl;
             }
 
+            bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
+
+            if (supportsAttachmentFeedbackLoop)
+            {
+                featuresAttachmentFeedbackLoop.PNext = features2.PNext;
+                features2.PNext = &featuresAttachmentFeedbackLoop;
+            }
+
+            bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
+
+            if (supportsDynamicAttachmentFeedbackLoop)
+            {
+                featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
+                features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
+            }
+
             bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
 
             if (usePortability)
@@ -401,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan
                 _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
                 _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
                 supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
+                supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
+                supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
                 propertiesSubgroup.SubgroupSize,
                 supportedSampleCounts,
                 portabilityFlags,
diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs
index efb0b31f9..d67362be3 100644
--- a/src/Ryujinx.Graphics.Vulkan/Window.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Window.cs
@@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan
                 SwizzleComponent.Blue,
                 SwizzleComponent.Alpha);
 
-            _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
+            _gd.SwapchainApi.CreateSwapchain(_device, in swapchainCreateInfo, null, out _swapchain).ThrowOnError();
 
             _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
 
@@ -187,14 +187,14 @@ namespace Ryujinx.Graphics.Vulkan
 
             for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
             {
-                _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
+                _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
             }
 
             _renderFinishedSemaphores = new Semaphore[imageCount];
 
             for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
             {
-                _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
+                _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
             }
         }
 
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
                 SubresourceRange = subresourceRange,
             };
 
-            _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
+            _gd.Api.CreateImageView(_device, in imageCreateInfo, null, out var imageView).ThrowOnError();
 
             return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format);
         }
@@ -479,7 +479,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             lock (_gd.QueueLock)
             {
-                _gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
+                _gd.SwapchainApi.QueuePresent(_gd.Queue, in presentInfo);
             }
         }
 
@@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Vulkan
                 0,
                 null,
                 1,
-                barrier);
+                in barrier);
         }
 
         private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs
index 745335ac9..2d350374b 100644
--- a/src/Ryujinx.Gtk3/Program.cs
+++ b/src/Ryujinx.Gtk3/Program.cs
@@ -4,6 +4,8 @@ using Ryujinx.Common.Configuration;
 using Ryujinx.Common.GraphicsDriver;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.SystemInterop;
+using Ryujinx.Common.Utilities;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
 using Ryujinx.Modules;
 using Ryujinx.SDL2.Common;
 using Ryujinx.UI;
@@ -40,9 +42,6 @@ namespace Ryujinx
         [LibraryImport("user32.dll", SetLastError = true)]
         public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
 
-        [LibraryImport("libc", SetLastError = true)]
-        private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
-
         private const uint MbIconWarning = 0x30;
 
         static Program()
@@ -104,12 +103,13 @@ namespace Ryujinx
                     throw new NotSupportedException("Failed to initialize multi-threading support.");
                 }
 
-                Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
-                setenv("GDK_BACKEND", "x11", 1);
+                OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11");
             }
 
             if (OperatingSystem.IsMacOS())
             {
+                MVKInitialization.InitializeResolver();
+
                 string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
                 string resourcesDataDir;
 
@@ -122,19 +122,13 @@ namespace Ryujinx
                     resourcesDataDir = baseDirectory;
                 }
 
-                static void SetEnvironmentVariableNoCaching(string key, string value)
-                {
-                    int res = setenv(key, value, 1);
-                    Debug.Assert(res != -1);
-                }
-
                 // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
-                SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
+                OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
 
                 // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
-                SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
+                OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
 
-                SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
+                OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
             }
 
             string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
@@ -230,9 +224,9 @@ namespace Ryujinx
             // Logging system information.
             PrintSystemInfo();
 
-            // Enable OGL multithreading on the driver, when available.
+            // Enable OGL multithreading on the driver, and some other flags.
             BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
-            DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
+            DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off);
 
             // Initialize Gtk.
             Application.Init();
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index 85aff6712..07995dbdd 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Configuration.Hid.Controller;
 using Ryujinx.Common.Configuration.Hid.Controller.Motion;
 using Ryujinx.Common.Configuration.Hid.Keyboard;
+using Ryujinx.Common.GraphicsDriver;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.Logging.Targets;
 using Ryujinx.Common.SystemInterop;
@@ -18,6 +19,7 @@ using Ryujinx.Graphics.Gpu;
 using Ryujinx.Graphics.Gpu.Shader;
 using Ryujinx.Graphics.OpenGL;
 using Ryujinx.Graphics.Vulkan;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
 using Ryujinx.Headless.SDL2.OpenGL;
 using Ryujinx.Headless.SDL2.Vulkan;
 using Ryujinx.HLE;
@@ -88,6 +90,11 @@ namespace Ryujinx.Headless.SDL2
                 };
             }
 
+            if (OperatingSystem.IsMacOS())
+            {
+                MVKInitialization.InitializeResolver();
+            }
+
             Parser.Default.ParseArguments<Options>(args)
             .WithParsed(Load)
             .WithNotParsed(errors => errors.Output());
@@ -457,6 +464,8 @@ namespace Ryujinx.Headless.SDL2
             GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
             GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
 
+            DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
+
             while (true)
             {
                 LoadApplication(option);
diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs
index 976963422..6c83cedcf 100644
--- a/src/Ryujinx/Program.cs
+++ b/src/Ryujinx/Program.cs
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration;
 using Ryujinx.Common.GraphicsDriver;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.SystemInterop;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
 using Ryujinx.Modules;
 using Ryujinx.SDL2.Common;
 using Ryujinx.UI.Common;
@@ -80,6 +81,11 @@ namespace Ryujinx.Ava
             // Parse arguments
             CommandLineState.ParseArguments(args);
 
+            if (OperatingSystem.IsMacOS())
+            {
+                MVKInitialization.InitializeResolver();
+            }
+
             // Delete backup files after updating.
             Task.Run(Updater.CleanupUpdate);
 
@@ -111,8 +117,8 @@ namespace Ryujinx.Ava
             // Logging system information.
             PrintSystemInfo();
 
-            // Enable OGL multithreading on the driver, when available.
-            DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
+            // Enable OGL multithreading on the driver, and some other flags.
+            DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
 
             // Check if keys exists.
             if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))