diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index 20bd87c6c8..54a9ae3bf4 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.GAL
         public bool HasVectorIndexingBug { get; }
 
         public bool SupportsAstcCompression { get; }
+        public bool SupportsR4G4Format { get; }
         public bool SupportsFragmentShaderInterlock { get; }
         public bool SupportsFragmentShaderOrderingIntel { get; }
         public bool SupportsImageLoadFormatted { get; }
@@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.GAL
             bool hasFrontFacingBug,
             bool hasVectorIndexingBug,
             bool supportsAstcCompression,
+            bool supportsR4G4Format,
             bool supportsFragmentShaderInterlock,
             bool supportsFragmentShaderOrderingIntel,
             bool supportsImageLoadFormatted,
@@ -40,6 +42,7 @@ namespace Ryujinx.Graphics.GAL
             HasFrontFacingBug = hasFrontFacingBug;
             HasVectorIndexingBug = hasVectorIndexingBug;
             SupportsAstcCompression = supportsAstcCompression;
+            SupportsR4G4Format = supportsR4G4Format;
             SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
             SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
             SupportsImageLoadFormatted = supportsImageLoadFormatted;
diff --git a/Ryujinx.Graphics.GAL/Format.cs b/Ryujinx.Graphics.GAL/Format.cs
index 98b6f5068f..d5e183bafe 100644
--- a/Ryujinx.Graphics.GAL/Format.cs
+++ b/Ryujinx.Graphics.GAL/Format.cs
@@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.GAL
         D32FloatS8Uint,
         R8G8B8X8Srgb,
         R8G8B8A8Srgb,
+        R4G4Unorm,
         R4G4B4A4Unorm,
         R5G5B5X1Unorm,
         R5G5B5A1Unorm,
diff --git a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
index 03687adee5..3c97e2e274 100644
--- a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
+++ b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
@@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.Gpu.Image
             { 0x25385, new FormatInfo(Format.D32FloatS8Uint,    1,  1,  8,  2) },
             { 0x253b0, new FormatInfo(Format.D32FloatS8Uint,    1,  1,  8,  2) },
             { 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb,      1,  1,  4,  4) },
+            { 0x2491e, new FormatInfo(Format.R4G4Unorm,         1,  1,  1,  2) },
             { 0x24912, new FormatInfo(Format.R4G4B4A4Unorm,     1,  1,  2,  4) },
             { 0x24914, new FormatInfo(Format.R5G5B5A1Unorm,     1,  1,  2,  4) },
             { 0x24915, new FormatInfo(Format.R5G6B5Unorm,       1,  1,  2,  3) },
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 590356e340..6d981479ab 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -785,7 +785,7 @@ namespace Ryujinx.Graphics.Gpu.Image
             // Handle compressed cases not supported by the host:
             // - ASTC is usually not supported on desktop cards.
             // - BC4/BC5 is not supported on 3D textures.
-            if (!_context.Capabilities.SupportsAstcCompression && Info.FormatInfo.Format.IsAstc())
+            if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
             {
                 if (!AstcDecoder.TryDecodeToRgba8P(
                     data.ToArray(),
@@ -805,11 +805,15 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                 data = decoded;
             }
-            else if (Target == Target.Texture3D && Info.FormatInfo.Format.IsBc4())
+            else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
+            {
+                data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
+            }
+            else if (Target == Target.Texture3D && Format.IsBc4())
             {
                 data = BCnDecoder.DecodeBC4(data, width, height, depth, levels, layers, Info.FormatInfo.Format == Format.Bc4Snorm);
             }
-            else if (Target == Target.Texture3D && Info.FormatInfo.Format.IsBc5())
+            else if (Target == Target.Texture3D && Format.IsBc5())
             {
                 data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Info.FormatInfo.Format == Format.Bc5Snorm);
             }
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index c70b297193..ce9fd75c77 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -66,6 +66,11 @@ namespace Ryujinx.Graphics.Gpu.Image
                 }
             }
 
+            if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
+            {
+                return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
+            }
+
             if (info.Target == Target.Texture3D)
             {
                 // The host API does not support 3D BC4/BC5 compressed formats.
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index 25b762b2af..0c16ec5a1a 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -101,21 +101,22 @@ namespace Ryujinx.Graphics.OpenGL
         public Capabilities GetCapabilities()
         {
             return new Capabilities(
-                HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
-                HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
-                HwCapabilities.SupportsAstcCompression,
-                HwCapabilities.SupportsFragmentShaderInterlock,
-                HwCapabilities.SupportsFragmentShaderOrdering,
-                HwCapabilities.SupportsImageLoadFormatted,
-                HwCapabilities.SupportsMismatchingViewFormat,
-                HwCapabilities.SupportsNonConstantTextureOffset,
-                HwCapabilities.SupportsShaderBallot,
-                HwCapabilities.SupportsTextureShadowLod,
-                HwCapabilities.SupportsViewportSwizzle,
-                HwCapabilities.SupportsIndirectParameters,
-                HwCapabilities.MaximumComputeSharedMemorySize,
-                HwCapabilities.MaximumSupportedAnisotropy,
-                HwCapabilities.StorageBufferOffsetAlignment);
+                hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
+                hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
+                supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
+                supportsR4G4Format: false,
+                supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
+                supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
+                supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
+                supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
+                supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
+                supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
+                supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
+                supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
+                supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
+                maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
+                maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
+                storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment);
         }
 
         public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
diff --git a/Ryujinx.Graphics.Texture/PixelConverter.cs b/Ryujinx.Graphics.Texture/PixelConverter.cs
new file mode 100644
index 0000000000..d7e45a6930
--- /dev/null
+++ b/Ryujinx.Graphics.Texture/PixelConverter.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace Ryujinx.Graphics.Texture
+{
+    public static class PixelConverter
+    {
+        public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data)
+        {
+            byte[] output = new byte[data.Length * 2];
+            int start = 0;
+
+            if (Sse41.IsSupported)
+            {
+                int sizeTrunc = data.Length & ~7;
+                start = sizeTrunc;
+
+                fixed (byte* inputPtr = data, outputPtr = output)
+                {
+                    for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
+                    {
+                        Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
+                    }
+                }
+            }
+
+            Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
+
+            for (int i = start; i < data.Length; i++)
+            {
+                outputSpan[i] = (ushort)data[i];
+            }
+
+            return output;
+        }
+    }
+}