aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2022-12-26 18:50:27 +0000
committerGitHub <noreply@github.com>2022-12-26 15:50:27 -0300
commit470be03c2ff22346a1f0ae53fa25f53c4d1790b5 (patch)
tree7ce75f2ac860063279893571c762257806e197b5
parentc963b3c80488f88c9d7f44588b6ba45051af3e2d (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.cs6
-rw-r--r--Ryujinx.Graphics.GAL/Format.cs21
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs35
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs21
-rw-r--r--Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs2
-rw-r--r--Ryujinx.Graphics.Texture/LayoutConverter.cs2
-rw-r--r--Ryujinx.Graphics.Texture/PixelConverter.cs199
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanRenderer.cs13
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 @@
1using Ryujinx.Common;
1using System; 2using System;
2using System.Runtime.InteropServices; 3using System.Runtime.InteropServices;
3using System.Runtime.Intrinsics; 4using 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,