diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs
index 45fc46ad..a030d8c8 100644
--- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs
@@ -168,5 +168,223 @@ namespace Ryujinx.Graphics.Vulkan
         {
             return _table[(int)format];
         }
+
+        public static int GetAttributeFormatSize(VkFormat format)
+        {
+            switch (format)
+            {
+                case VkFormat.R8Unorm:
+                case VkFormat.R8SNorm:
+                case VkFormat.R8Uint:
+                case VkFormat.R8Sint:
+                case VkFormat.R8Uscaled:
+                case VkFormat.R8Sscaled:
+                    return 1;
+
+                case VkFormat.R8G8Unorm:
+                case VkFormat.R8G8SNorm:
+                case VkFormat.R8G8Uint:
+                case VkFormat.R8G8Sint:
+                case VkFormat.R8G8Uscaled:
+                case VkFormat.R8G8Sscaled:
+                case VkFormat.R16Sfloat:
+                case VkFormat.R16Unorm:
+                case VkFormat.R16SNorm:
+                case VkFormat.R16Uint:
+                case VkFormat.R16Sint:
+                case VkFormat.R16Uscaled:
+                case VkFormat.R16Sscaled:
+                    return 2;
+
+                case VkFormat.R8G8B8Unorm:
+                case VkFormat.R8G8B8SNorm:
+                case VkFormat.R8G8B8Uint:
+                case VkFormat.R8G8B8Sint:
+                case VkFormat.R8G8B8Uscaled:
+                case VkFormat.R8G8B8Sscaled:
+                    return 3;
+
+                case VkFormat.R8G8B8A8Unorm:
+                case VkFormat.R8G8B8A8SNorm:
+                case VkFormat.R8G8B8A8Uint:
+                case VkFormat.R8G8B8A8Sint:
+                case VkFormat.R8G8B8A8Srgb:
+                case VkFormat.R8G8B8A8Uscaled:
+                case VkFormat.R8G8B8A8Sscaled:
+                case VkFormat.B8G8R8A8Unorm:
+                case VkFormat.B8G8R8A8Srgb:
+                case VkFormat.R16G16Sfloat:
+                case VkFormat.R16G16Unorm:
+                case VkFormat.R16G16SNorm:
+                case VkFormat.R16G16Uint:
+                case VkFormat.R16G16Sint:
+                case VkFormat.R16G16Uscaled:
+                case VkFormat.R16G16Sscaled:
+                case VkFormat.R32Sfloat:
+                case VkFormat.R32Uint:
+                case VkFormat.R32Sint:
+                case VkFormat.A2B10G10R10UnormPack32:
+                case VkFormat.A2B10G10R10UintPack32:
+                case VkFormat.B10G11R11UfloatPack32:
+                case VkFormat.E5B9G9R9UfloatPack32:
+                case VkFormat.A2B10G10R10SNormPack32:
+                case VkFormat.A2B10G10R10SintPack32:
+                case VkFormat.A2B10G10R10UscaledPack32:
+                case VkFormat.A2B10G10R10SscaledPack32:
+                    return 4;
+
+                case VkFormat.R16G16B16Sfloat:
+                case VkFormat.R16G16B16Unorm:
+                case VkFormat.R16G16B16SNorm:
+                case VkFormat.R16G16B16Uint:
+                case VkFormat.R16G16B16Sint:
+                case VkFormat.R16G16B16Uscaled:
+                case VkFormat.R16G16B16Sscaled:
+                    return 6;
+
+                case VkFormat.R16G16B16A16Sfloat:
+                case VkFormat.R16G16B16A16Unorm:
+                case VkFormat.R16G16B16A16SNorm:
+                case VkFormat.R16G16B16A16Uint:
+                case VkFormat.R16G16B16A16Sint:
+                case VkFormat.R16G16B16A16Uscaled:
+                case VkFormat.R16G16B16A16Sscaled:
+                case VkFormat.R32G32Sfloat:
+                case VkFormat.R32G32Uint:
+                case VkFormat.R32G32Sint:
+                    return 8;
+
+                case VkFormat.R32G32B32Sfloat:
+                case VkFormat.R32G32B32Uint:
+                case VkFormat.R32G32B32Sint:
+                    return 12;
+
+                case VkFormat.R32G32B32A32Sfloat:
+                case VkFormat.R32G32B32A32Uint:
+                case VkFormat.R32G32B32A32Sint:
+                    return 16;
+            }
+
+            return 1;
+        }
+
+        public static VkFormat DropLastComponent(VkFormat format)
+        {
+            switch (format)
+            {
+                case VkFormat.R8G8Unorm:
+                    return VkFormat.R8Unorm;
+                case VkFormat.R8G8SNorm:
+                    return VkFormat.R8SNorm;
+                case VkFormat.R8G8Uint:
+                    return VkFormat.R8Uint;
+                case VkFormat.R8G8Sint:
+                    return VkFormat.R8Sint;
+                case VkFormat.R8G8Uscaled:
+                    return VkFormat.R8Uscaled;
+                case VkFormat.R8G8Sscaled:
+                    return VkFormat.R8Sscaled;
+
+                case VkFormat.R8G8B8Unorm:
+                    return VkFormat.R8G8Unorm;
+                case VkFormat.R8G8B8SNorm:
+                    return VkFormat.R8G8SNorm;
+                case VkFormat.R8G8B8Uint:
+                    return VkFormat.R8G8Uint;
+                case VkFormat.R8G8B8Sint:
+                    return VkFormat.R8G8Sint;
+                case VkFormat.R8G8B8Uscaled:
+                    return VkFormat.R8G8Uscaled;
+                case VkFormat.R8G8B8Sscaled:
+                    return VkFormat.R8G8Sscaled;
+
+                case VkFormat.R8G8B8A8Unorm:
+                    return VkFormat.R8G8B8Unorm;
+                case VkFormat.R8G8B8A8SNorm:
+                    return VkFormat.R8G8B8SNorm;
+                case VkFormat.R8G8B8A8Uint:
+                    return VkFormat.R8G8B8Uint;
+                case VkFormat.R8G8B8A8Sint:
+                    return VkFormat.R8G8B8Sint;
+                case VkFormat.R8G8B8A8Srgb:
+                    return VkFormat.R8G8B8Srgb;
+                case VkFormat.R8G8B8A8Uscaled:
+                    return VkFormat.R8G8B8Uscaled;
+                case VkFormat.R8G8B8A8Sscaled:
+                    return VkFormat.R8G8B8Sscaled;
+                case VkFormat.B8G8R8A8Unorm:
+                    return VkFormat.B8G8R8Unorm;
+                case VkFormat.B8G8R8A8Srgb:
+                    return VkFormat.B8G8R8Srgb;
+
+                case VkFormat.R16G16Sfloat:
+                    return VkFormat.R16Sfloat;
+                case VkFormat.R16G16Unorm:
+                    return VkFormat.R16Unorm;
+                case VkFormat.R16G16SNorm:
+                    return VkFormat.R16SNorm;
+                case VkFormat.R16G16Uint:
+                    return VkFormat.R16Uint;
+                case VkFormat.R16G16Sint:
+                    return VkFormat.R16Sint;
+                case VkFormat.R16G16Uscaled:
+                    return VkFormat.R16Uscaled;
+                case VkFormat.R16G16Sscaled:
+                    return VkFormat.R16Sscaled;
+
+                case VkFormat.R16G16B16Sfloat:
+                    return VkFormat.R16G16Sfloat;
+                case VkFormat.R16G16B16Unorm:
+                    return VkFormat.R16G16Unorm;
+                case VkFormat.R16G16B16SNorm:
+                    return VkFormat.R16G16SNorm;
+                case VkFormat.R16G16B16Uint:
+                    return VkFormat.R16G16Uint;
+                case VkFormat.R16G16B16Sint:
+                    return VkFormat.R16G16Sint;
+                case VkFormat.R16G16B16Uscaled:
+                    return VkFormat.R16G16Uscaled;
+                case VkFormat.R16G16B16Sscaled:
+                    return VkFormat.R16G16Sscaled;
+
+                case VkFormat.R16G16B16A16Sfloat:
+                    return VkFormat.R16G16B16Sfloat;
+                case VkFormat.R16G16B16A16Unorm:
+                    return VkFormat.R16G16B16Unorm;
+                case VkFormat.R16G16B16A16SNorm:
+                    return VkFormat.R16G16B16SNorm;
+                case VkFormat.R16G16B16A16Uint:
+                    return VkFormat.R16G16B16Uint;
+                case VkFormat.R16G16B16A16Sint:
+                    return VkFormat.R16G16B16Sint;
+                case VkFormat.R16G16B16A16Uscaled:
+                    return VkFormat.R16G16B16Uscaled;
+                case VkFormat.R16G16B16A16Sscaled:
+                    return VkFormat.R16G16B16Sscaled;
+
+                case VkFormat.R32G32Sfloat:
+                    return VkFormat.R32Sfloat;
+                case VkFormat.R32G32Uint:
+                    return VkFormat.R32Uint;
+                case VkFormat.R32G32Sint:
+                    return VkFormat.R32Sint;
+
+                case VkFormat.R32G32B32Sfloat:
+                    return VkFormat.R32G32Sfloat;
+                case VkFormat.R32G32B32Uint:
+                    return VkFormat.R32G32Uint;
+                case VkFormat.R32G32B32Sint:
+                    return VkFormat.R32G32Sint;
+
+                case VkFormat.R32G32B32A32Sfloat:
+                    return VkFormat.R32G32B32Sfloat;
+                case VkFormat.R32G32B32A32Uint:
+                    return VkFormat.R32G32B32Uint;
+                case VkFormat.R32G32B32A32Sint:
+                    return VkFormat.R32G32B32Sint;
+            }
+
+            return format;
+        }
     }
 }
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
index dccc8ce6..a3d8dd6f 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
@@ -1,4 +1,5 @@
-using Silk.NET.Vulkan;
+using Ryujinx.Common.Memory;
+using Silk.NET.Vulkan;
 using System;
 
 namespace Ryujinx.Graphics.Vulkan
@@ -308,6 +309,8 @@ namespace Ryujinx.Graphics.Vulkan
         public PipelineLayout PipelineLayout;
         public SpecData SpecializationData;
 
+        private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2;
+
         public void Initialize()
         {
             Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
@@ -400,7 +403,15 @@ namespace Ryujinx.Graphics.Vulkan
 
             Pipeline pipelineHandle = default;
 
+            bool isMoltenVk = gd.IsMoltenVk;
+
+            if (isMoltenVk)
+            {
+                UpdateVertexAttributeDescriptions();
+            }
+
             fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
+            fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
             fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
             fixed (Viewport* pViewports = &Internal.Viewports[0])
             fixed (Rect2D* pScissors = &Internal.Scissors[0])
@@ -410,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
                 {
                     SType = StructureType.PipelineVertexInputStateCreateInfo,
                     VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
-                    PVertexAttributeDescriptions = pVertexAttributeDescriptions,
+                    PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
                     VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
                     PVertexBindingDescriptions = pVertexBindingDescriptions
                 };
@@ -612,6 +623,40 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
+        private void UpdateVertexAttributeDescriptions()
+        {
+            // Vertex attributes exceeding the stride are invalid.
+            // In metal, they cause glitches with the vertex shader fetching incorrect values.
+            // To work around this, we reduce the format to something that doesn't exceed the stride if possible.
+            // The assumption is that the exceeding components are not actually accessed on the shader.
+
+            for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
+            {
+                var attribute = Internal.VertexAttributeDescriptions[index];
+                ref var vb = ref Internal.VertexBindingDescriptions[(int)attribute.Binding];
+
+                Format format = attribute.Format;
+
+                while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride)
+                {
+                    Format newFormat = FormatTable.DropLastComponent(format);
+
+                    if (newFormat == format)
+                    {
+                        // That case means we failed to find a format that fits within the stride,
+                        // so just restore the original format and give up.
+                        format = attribute.Format;
+                        break;
+                    }
+
+                    format = newFormat;
+                }
+
+                attribute.Format = format;
+                _vertexAttributeDescriptions2[index] = attribute;
+            }
+        }
+
         public void Dispose()
         {
             Stages.Dispose();