diff options
author | riperiperi <rhy3756547@hotmail.com> | 2022-12-26 18:50:27 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-26 15:50:27 -0300 |
commit | 470be03c2ff22346a1f0ae53fa25f53c4d1790b5 (patch) | |
tree | 7ce75f2ac860063279893571c762257806e197b5 | |
parent | c963b3c80488f88c9d7f44588b6ba45051af3e2d (diff) |
GPU: Add fallback when 16-bit formats are not supported (#4108)1.1.492
* Add conversion for 16 bit RGBA formats (not supported in Rosetta)
* Rebase fix
Rebase fix
* Forgot to remove this
* Fix RGBA16 format conversion
* Add RGBA4 -> RGBA8 conversion
* Handle host stride alignment
* Address Feedback Part 1
* Can't count
* Don't zero out rgb when alpha is 0
* Separate RGBA4 and 5-bit component formats
Not sure of a better way to name them...
* Add A1B5G5R5 conversion
* Put this in the right place.
* Make format naming consistent for capabilities
* Change method names
-rw-r--r-- | Ryujinx.Graphics.GAL/Capabilities.cs | 6 | ||||
-rw-r--r-- | Ryujinx.Graphics.GAL/Format.cs | 21 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Texture.cs | 35 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs | 21 | ||||
-rw-r--r-- | Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 2 | ||||
-rw-r--r-- | Ryujinx.Graphics.Texture/LayoutConverter.cs | 2 | ||||
-rw-r--r-- | Ryujinx.Graphics.Texture/PixelConverter.cs | 199 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 13 |
8 files changed, 285 insertions, 14 deletions
diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index bc93908b1..abacdcfa6 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs | |||
@@ -18,7 +18,9 @@ namespace Ryujinx.Graphics.GAL | |||
18 | public readonly bool Supports3DTextureCompression; | 18 | public readonly bool Supports3DTextureCompression; |
19 | public readonly bool SupportsBgraFormat; | 19 | public readonly bool SupportsBgraFormat; |
20 | public readonly bool SupportsR4G4Format; | 20 | public readonly bool SupportsR4G4Format; |
21 | public readonly bool SupportsR4G4B4A4Format; | ||
21 | public readonly bool SupportsSnormBufferTextureFormat; | 22 | public readonly bool SupportsSnormBufferTextureFormat; |
23 | public readonly bool Supports5BitComponentFormat; | ||
22 | public readonly bool SupportsFragmentShaderInterlock; | 24 | public readonly bool SupportsFragmentShaderInterlock; |
23 | public readonly bool SupportsFragmentShaderOrderingIntel; | 25 | public readonly bool SupportsFragmentShaderOrderingIntel; |
24 | public readonly bool SupportsGeometryShaderPassthrough; | 26 | public readonly bool SupportsGeometryShaderPassthrough; |
@@ -55,7 +57,9 @@ namespace Ryujinx.Graphics.GAL | |||
55 | bool supports3DTextureCompression, | 57 | bool supports3DTextureCompression, |
56 | bool supportsBgraFormat, | 58 | bool supportsBgraFormat, |
57 | bool supportsR4G4Format, | 59 | bool supportsR4G4Format, |
60 | bool supportsR4G4B4A4Format, | ||
58 | bool supportsSnormBufferTextureFormat, | 61 | bool supportsSnormBufferTextureFormat, |
62 | bool supports5BitComponentFormat, | ||
59 | bool supportsFragmentShaderInterlock, | 63 | bool supportsFragmentShaderInterlock, |
60 | bool supportsFragmentShaderOrderingIntel, | 64 | bool supportsFragmentShaderOrderingIntel, |
61 | bool supportsGeometryShaderPassthrough, | 65 | bool supportsGeometryShaderPassthrough, |
@@ -89,7 +93,9 @@ namespace Ryujinx.Graphics.GAL | |||
89 | Supports3DTextureCompression = supports3DTextureCompression; | 93 | Supports3DTextureCompression = supports3DTextureCompression; |
90 | SupportsBgraFormat = supportsBgraFormat; | 94 | SupportsBgraFormat = supportsBgraFormat; |
91 | SupportsR4G4Format = supportsR4G4Format; | 95 | SupportsR4G4Format = supportsR4G4Format; |
96 | SupportsR4G4B4A4Format = supportsR4G4B4A4Format; | ||
92 | SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; | 97 | SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; |
98 | Supports5BitComponentFormat = supports5BitComponentFormat; | ||
93 | SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; | 99 | SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; |
94 | SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; | 100 | SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; |
95 | SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; | 101 | SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; |
diff --git a/Ryujinx.Graphics.GAL/Format.cs b/Ryujinx.Graphics.GAL/Format.cs index 87d08803d..5e0274e58 100644 --- a/Ryujinx.Graphics.GAL/Format.cs +++ b/Ryujinx.Graphics.GAL/Format.cs | |||
@@ -449,6 +449,27 @@ namespace Ryujinx.Graphics.GAL | |||
449 | } | 449 | } |
450 | 450 | ||
451 | /// <summary> | 451 | /// <summary> |
452 | /// Checks if the texture format is 16 bit packed. | ||
453 | /// </summary> | ||
454 | /// <param name="format">Texture format</param> | ||
455 | /// <returns>True if the texture format is 16 bit packed, false otherwise</returns> | ||
456 | public static bool Is16BitPacked(this Format format) | ||
457 | { | ||
458 | switch (format) | ||
459 | { | ||
460 | case Format.B5G6R5Unorm: | ||
461 | case Format.B5G5R5A1Unorm: | ||
462 | case Format.R5G5B5X1Unorm: | ||
463 | case Format.R5G5B5A1Unorm: | ||
464 | case Format.R5G6B5Unorm: | ||
465 | case Format.R4G4B4A4Unorm: | ||
466 | return true; | ||
467 | } | ||
468 | |||
469 | return false; | ||
470 | } | ||
471 | |||
472 | /// <summary> | ||
452 | /// Checks if the texture format is an ASTC format. | 473 | /// Checks if the texture format is an ASTC format. |
453 | /// </summary> | 474 | /// </summary> |
454 | /// <param name="format">Texture format</param> | 475 | /// <param name="format">Texture format</param> |
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 904c908f2..0995314d0 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs | |||
@@ -911,7 +911,40 @@ namespace Ryujinx.Graphics.Gpu.Image | |||
911 | } | 911 | } |
912 | else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm) | 912 | else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm) |
913 | { | 913 | { |
914 | result = PixelConverter.ConvertR4G4ToR4G4B4A4(result); | 914 | result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width); |
915 | |||
916 | if (!_context.Capabilities.SupportsR4G4B4A4Format) | ||
917 | { | ||
918 | result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width); | ||
919 | } | ||
920 | } | ||
921 | else if (Format == Format.R4G4B4A4Unorm) | ||
922 | { | ||
923 | if (!_context.Capabilities.SupportsR4G4B4A4Format) | ||
924 | { | ||
925 | result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width); | ||
926 | } | ||
927 | } | ||
928 | else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked()) | ||
929 | { | ||
930 | switch (Format) | ||
931 | { | ||
932 | case Format.B5G6R5Unorm: | ||
933 | case Format.R5G6B5Unorm: | ||
934 | result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width); | ||
935 | break; | ||
936 | case Format.B5G5R5A1Unorm: | ||
937 | case Format.R5G5B5X1Unorm: | ||
938 | case Format.R5G5B5A1Unorm: | ||
939 | result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm); | ||
940 | break; | ||
941 | case Format.A1B5G5R5Unorm: | ||
942 | result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width); | ||
943 | break; | ||
944 | case Format.R4G4B4A4Unorm: | ||
945 | result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width); | ||
946 | break; | ||
947 | } | ||
915 | } | 948 | } |
916 | 949 | ||
917 | return result; | 950 | return result; |
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 642e03b68..7ec4c7ac2 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs | |||
@@ -132,7 +132,26 @@ namespace Ryujinx.Graphics.Gpu.Image | |||
132 | 132 | ||
133 | if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm) | 133 | if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm) |
134 | { | 134 | { |
135 | return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4); | 135 | if (caps.SupportsR4G4B4A4Format) |
136 | { | ||
137 | return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4); | ||
138 | } | ||
139 | else | ||
140 | { | ||
141 | return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | if (info.FormatInfo.Format == Format.R4G4B4A4Unorm) | ||
146 | { | ||
147 | if (!caps.SupportsR4G4B4A4Format) | ||
148 | { | ||
149 | return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4); | ||
150 | } | ||
151 | } | ||
152 | else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked()) | ||
153 | { | ||
154 | return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4); | ||
136 | } | 155 | } |
137 | 156 | ||
138 | return info.FormatInfo; | 157 | return info.FormatInfo; |
diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 59ca6afdc..de1ce4a34 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | |||
@@ -114,7 +114,9 @@ namespace Ryujinx.Graphics.OpenGL | |||
114 | supports3DTextureCompression: false, | 114 | supports3DTextureCompression: false, |
115 | supportsBgraFormat: false, | 115 | supportsBgraFormat: false, |
116 | supportsR4G4Format: false, | 116 | supportsR4G4Format: false, |
117 | supportsR4G4B4A4Format: true, | ||
117 | supportsSnormBufferTextureFormat: false, | 118 | supportsSnormBufferTextureFormat: false, |
119 | supports5BitComponentFormat: true, | ||
118 | supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, | 120 | supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, |
119 | supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, | 121 | supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, |
120 | supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, | 122 | supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, |
diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/Ryujinx.Graphics.Texture/LayoutConverter.cs index 188ae0c15..b8ec9748b 100644 --- a/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/Ryujinx.Graphics.Texture/LayoutConverter.cs | |||
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Texture | |||
7 | { | 7 | { |
8 | public static class LayoutConverter | 8 | public static class LayoutConverter |
9 | { | 9 | { |
10 | private const int HostStrideAlignment = 4; | 10 | public const int HostStrideAlignment = 4; |
11 | 11 | ||
12 | public static void ConvertBlockLinearToLinear( | 12 | public static void ConvertBlockLinearToLinear( |
13 | Span<byte> dst, | 13 | Span<byte> dst, |
diff --git a/Ryujinx.Graphics.Texture/PixelConverter.cs b/Ryujinx.Graphics.Texture/PixelConverter.cs index d7e45a693..add25cd35 100644 --- a/Ryujinx.Graphics.Texture/PixelConverter.cs +++ b/Ryujinx.Graphics.Texture/PixelConverter.cs | |||
@@ -1,3 +1,4 @@ | |||
1 | using Ryujinx.Common; | ||
1 | using System; | 2 | using System; |
2 | using System.Runtime.InteropServices; | 3 | using System.Runtime.InteropServices; |
3 | using System.Runtime.Intrinsics; | 4 | using System.Runtime.Intrinsics; |
@@ -7,30 +8,206 @@ namespace Ryujinx.Graphics.Texture | |||
7 | { | 8 | { |
8 | public static class PixelConverter | 9 | public static class PixelConverter |
9 | { | 10 | { |
10 | public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data) | 11 | private static (int remainder, int outRemainder, int height) GetLineRemainders(int length, int width, int bpp, int outBpp) |
12 | { | ||
13 | int stride = BitUtils.AlignUp(width * bpp, LayoutConverter.HostStrideAlignment); | ||
14 | int remainder = stride / bpp - width; | ||
15 | |||
16 | int outStride = BitUtils.AlignUp(width * outBpp, LayoutConverter.HostStrideAlignment); | ||
17 | int outRemainder = outStride / outBpp - width; | ||
18 | |||
19 | return (remainder, outRemainder, length / stride); | ||
20 | } | ||
21 | |||
22 | public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width) | ||
11 | { | 23 | { |
12 | byte[] output = new byte[data.Length * 2]; | 24 | byte[] output = new byte[data.Length * 2]; |
13 | int start = 0; | ||
14 | 25 | ||
15 | if (Sse41.IsSupported) | 26 | (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2); |
27 | |||
28 | Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output); | ||
29 | |||
30 | if (remainder == 0) | ||
31 | { | ||
32 | int start = 0; | ||
33 | |||
34 | if (Sse41.IsSupported) | ||
35 | { | ||
36 | int sizeTrunc = data.Length & ~7; | ||
37 | start = sizeTrunc; | ||
38 | |||
39 | fixed (byte* inputPtr = data, outputPtr = output) | ||
40 | { | ||
41 | for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8) | ||
42 | { | ||
43 | Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte()); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | for (int i = start; i < data.Length; i++) | ||
49 | { | ||
50 | outputSpan[i] = (ushort)data[i]; | ||
51 | } | ||
52 | } | ||
53 | else | ||
16 | { | 54 | { |
17 | int sizeTrunc = data.Length & ~7; | 55 | int offset = 0; |
18 | start = sizeTrunc; | 56 | int outOffset = 0; |
19 | 57 | ||
20 | fixed (byte* inputPtr = data, outputPtr = output) | 58 | for (int y = 0; y < height; y++) |
21 | { | 59 | { |
22 | for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8) | 60 | for (int x = 0; x < width; x++) |
23 | { | 61 | { |
24 | Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte()); | 62 | outputSpan[outOffset++] = data[offset++]; |
25 | } | 63 | } |
64 | |||
65 | offset += remainder; | ||
66 | outOffset += outRemainder; | ||
26 | } | 67 | } |
27 | } | 68 | } |
28 | 69 | ||
29 | Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output); | 70 | return output; |
71 | } | ||
72 | |||
73 | public unsafe static byte[] ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width) | ||
74 | { | ||
75 | byte[] output = new byte[data.Length * 2]; | ||
76 | int offset = 0; | ||
77 | int outOffset = 0; | ||
78 | |||
79 | (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); | ||
80 | |||
81 | ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); | ||
82 | Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output); | ||
30 | 83 | ||
31 | for (int i = start; i < data.Length; i++) | 84 | for (int y = 0; y < height; y++) |
32 | { | 85 | { |
33 | outputSpan[i] = (ushort)data[i]; | 86 | for (int x = 0; x < width; x++) |
87 | { | ||
88 | uint packed = inputSpan[offset++]; | ||
89 | |||
90 | uint outputPacked = 0xff000000; | ||
91 | outputPacked |= (packed << 3) & 0x000000f8; | ||
92 | outputPacked |= (packed << 8) & 0x00f80000; | ||
93 | |||
94 | // Replicate 5 bit components. | ||
95 | outputPacked |= (outputPacked >> 5) & 0x00070007; | ||
96 | |||
97 | // Include and replicate 6 bit component. | ||
98 | outputPacked |= ((packed << 5) & 0x0000fc00) | ((packed >> 1) & 0x00000300); | ||
99 | |||
100 | outputSpan[outOffset++] = outputPacked; | ||
101 | } | ||
102 | |||
103 | offset += remainder; | ||
104 | outOffset += outRemainder; | ||
105 | } | ||
106 | |||
107 | return output; | ||
108 | } | ||
109 | |||
110 | public unsafe static byte[] ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha) | ||
111 | { | ||
112 | byte[] output = new byte[data.Length * 2]; | ||
113 | int offset = 0; | ||
114 | int outOffset = 0; | ||
115 | |||
116 | (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); | ||
117 | |||
118 | ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); | ||
119 | Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output); | ||
120 | |||
121 | for (int y = 0; y < height; y++) | ||
122 | { | ||
123 | for (int x = 0; x < width; x++) | ||
124 | { | ||
125 | uint packed = inputSpan[offset++]; | ||
126 | |||
127 | uint a = forceAlpha ? 1 : (packed >> 15); | ||
128 | |||
129 | uint outputPacked = a * 0xff000000; | ||
130 | outputPacked |= (packed << 3) & 0x000000f8; | ||
131 | outputPacked |= (packed << 6) & 0x0000f800; | ||
132 | outputPacked |= (packed << 9) & 0x00f80000; | ||
133 | |||
134 | // Replicate 5 bit components. | ||
135 | outputPacked |= (outputPacked >> 5) & 0x00070707; | ||
136 | |||
137 | outputSpan[outOffset++] = outputPacked; | ||
138 | } | ||
139 | |||
140 | offset += remainder; | ||
141 | outOffset += outRemainder; | ||
142 | } | ||
143 | |||
144 | return output; | ||
145 | } | ||
146 | |||
147 | public unsafe static byte[] ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width) | ||
148 | { | ||
149 | byte[] output = new byte[data.Length * 2]; | ||
150 | int offset = 0; | ||
151 | int outOffset = 0; | ||
152 | |||
153 | (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); | ||
154 | |||
155 | ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); | ||
156 | Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output); | ||
157 | |||
158 | for (int y = 0; y < height; y++) | ||
159 | { | ||
160 | for (int x = 0; x < width; x++) | ||
161 | { | ||
162 | uint packed = inputSpan[offset++]; | ||
163 | |||
164 | uint a = packed >> 15; | ||
165 | |||
166 | uint outputPacked = a * 0xff000000; | ||
167 | outputPacked |= (packed >> 8) & 0x000000f8; | ||
168 | outputPacked |= (packed << 5) & 0x0000f800; | ||
169 | outputPacked |= (packed << 18) & 0x00f80000; | ||
170 | |||
171 | // Replicate 5 bit components. | ||
172 | outputPacked |= (outputPacked >> 5) & 0x00070707; | ||
173 | |||
174 | outputSpan[outOffset++] = outputPacked; | ||
175 | } | ||
176 | |||
177 | offset += remainder; | ||
178 | outOffset += outRemainder; | ||
179 | } | ||
180 | |||
181 | return output; | ||
182 | } | ||
183 | |||
184 | public unsafe static byte[] ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width) | ||
185 | { | ||
186 | byte[] output = new byte[data.Length * 2]; | ||
187 | int offset = 0; | ||
188 | int outOffset = 0; | ||
189 | |||
190 | (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); | ||
191 | |||
192 | ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); | ||
193 | Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output); | ||
194 | |||
195 | for (int y = 0; y < height; y++) | ||
196 | { | ||
197 | for (int x = 0; x < width; x++) | ||
198 | { | ||
199 | uint packed = inputSpan[offset++]; | ||
200 | |||
201 | uint outputPacked = packed & 0x0000000f; | ||
202 | outputPacked |= (packed << 4) & 0x00000f00; | ||
203 | outputPacked |= (packed << 8) & 0x000f0000; | ||
204 | outputPacked |= (packed << 12) & 0x0f000000; | ||
205 | |||
206 | outputSpan[outOffset++] = outputPacked * 0x11; | ||
207 | } | ||
208 | |||
209 | offset += remainder; | ||
210 | outOffset += outRemainder; | ||
34 | } | 211 | } |
35 | 212 | ||
36 | return output; | 213 | return output; |
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index a32400f56..3c446abf3 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | |||
@@ -396,6 +396,17 @@ namespace Ryujinx.Graphics.Vulkan | |||
396 | GAL.Format.Etc2RgbSrgb, | 396 | GAL.Format.Etc2RgbSrgb, |
397 | GAL.Format.Etc2RgbUnorm); | 397 | GAL.Format.Etc2RgbUnorm); |
398 | 398 | ||
399 | bool supports5BitComponentFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, | ||
400 | GAL.Format.R5G6B5Unorm, | ||
401 | GAL.Format.R5G5B5A1Unorm, | ||
402 | GAL.Format.R5G5B5X1Unorm, | ||
403 | GAL.Format.B5G6R5Unorm, | ||
404 | GAL.Format.B5G5R5A1Unorm, | ||
405 | GAL.Format.A1B5G5R5Unorm); | ||
406 | |||
407 | bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, | ||
408 | GAL.Format.R4G4B4A4Unorm); | ||
409 | |||
399 | PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() | 410 | PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() |
400 | { | 411 | { |
401 | SType = StructureType.PhysicalDeviceVulkan12Features | 412 | SType = StructureType.PhysicalDeviceVulkan12Features |
@@ -425,7 +436,9 @@ namespace Ryujinx.Graphics.Vulkan | |||
425 | supports3DTextureCompression: true, | 436 | supports3DTextureCompression: true, |
426 | supportsBgraFormat: true, | 437 | supportsBgraFormat: true, |
427 | supportsR4G4Format: false, | 438 | supportsR4G4Format: false, |
439 | supportsR4G4B4A4Format: supportsR4G4B4A4Format, | ||
428 | supportsSnormBufferTextureFormat: true, | 440 | supportsSnormBufferTextureFormat: true, |
441 | supports5BitComponentFormat: supports5BitComponentFormat, | ||
429 | supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, | 442 | supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, |
430 | supportsFragmentShaderOrderingIntel: false, | 443 | supportsFragmentShaderOrderingIntel: false, |
431 | supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, | 444 | supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, |