aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Texture
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 /Ryujinx.Graphics.Texture
parentc963b3c80488f88c9d7f44588b6ba45051af3e2d (diff)
GPU: Add fallback when 16-bit formats are not supported (#4108)
* 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
Diffstat (limited to 'Ryujinx.Graphics.Texture')
-rw-r--r--Ryujinx.Graphics.Texture/LayoutConverter.cs2
-rw-r--r--Ryujinx.Graphics.Texture/PixelConverter.cs199
2 files changed, 189 insertions, 12 deletions
diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/Ryujinx.Graphics.Texture/LayoutConverter.cs
index 188ae0c1..b8ec9748 100644
--- a/Ryujinx.Graphics.Texture/LayoutConverter.cs
+++ b/Ryujinx.Graphics.Texture/LayoutConverter.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Texture
{
public static class LayoutConverter
{
- private const int HostStrideAlignment = 4;
+ public const int HostStrideAlignment = 4;
public static void ConvertBlockLinearToLinear(
Span<byte> dst,
diff --git a/Ryujinx.Graphics.Texture/PixelConverter.cs b/Ryujinx.Graphics.Texture/PixelConverter.cs
index d7e45a69..add25cd3 100644
--- a/Ryujinx.Graphics.Texture/PixelConverter.cs
+++ b/Ryujinx.Graphics.Texture/PixelConverter.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@@ -7,30 +8,206 @@ namespace Ryujinx.Graphics.Texture
{
public static class PixelConverter
{
- public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data)
+ private static (int remainder, int outRemainder, int height) GetLineRemainders(int length, int width, int bpp, int outBpp)
+ {
+ int stride = BitUtils.AlignUp(width * bpp, LayoutConverter.HostStrideAlignment);
+ int remainder = stride / bpp - width;
+
+ int outStride = BitUtils.AlignUp(width * outBpp, LayoutConverter.HostStrideAlignment);
+ int outRemainder = outStride / outBpp - width;
+
+ return (remainder, outRemainder, length / stride);
+ }
+
+ public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
- int start = 0;
- if (Sse41.IsSupported)
+ (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
+
+ Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
+
+ if (remainder == 0)
+ {
+ 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());
+ }
+ }
+ }
+
+ for (int i = start; i < data.Length; i++)
+ {
+ outputSpan[i] = (ushort)data[i];
+ }
+ }
+ else
{
- int sizeTrunc = data.Length & ~7;
- start = sizeTrunc;
+ int offset = 0;
+ int outOffset = 0;
- fixed (byte* inputPtr = data, outputPtr = output)
+ for (int y = 0; y < height; y++)
{
- for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
+ for (int x = 0; x < width; x++)
{
- Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
+ outputSpan[outOffset++] = data[offset++];
}
+
+ offset += remainder;
+ outOffset += outRemainder;
}
}
- Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
+ return output;
+ }
+
+ public unsafe static byte[] ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
+ {
+ byte[] output = new byte[data.Length * 2];
+ int offset = 0;
+ int outOffset = 0;
+
+ (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
+
+ ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
+ Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
- for (int i = start; i < data.Length; i++)
+ for (int y = 0; y < height; y++)
{
- outputSpan[i] = (ushort)data[i];
+ for (int x = 0; x < width; x++)
+ {
+ uint packed = inputSpan[offset++];
+
+ uint outputPacked = 0xff000000;
+ outputPacked |= (packed << 3) & 0x000000f8;
+ outputPacked |= (packed << 8) & 0x00f80000;
+
+ // Replicate 5 bit components.
+ outputPacked |= (outputPacked >> 5) & 0x00070007;
+
+ // Include and replicate 6 bit component.
+ outputPacked |= ((packed << 5) & 0x0000fc00) | ((packed >> 1) & 0x00000300);
+
+ outputSpan[outOffset++] = outputPacked;
+ }
+
+ offset += remainder;
+ outOffset += outRemainder;
+ }
+
+ return output;
+ }
+
+ public unsafe static byte[] ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
+ {
+ byte[] output = new byte[data.Length * 2];
+ int offset = 0;
+ int outOffset = 0;
+
+ (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
+
+ ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
+ Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ uint packed = inputSpan[offset++];
+
+ uint a = forceAlpha ? 1 : (packed >> 15);
+
+ uint outputPacked = a * 0xff000000;
+ outputPacked |= (packed << 3) & 0x000000f8;
+ outputPacked |= (packed << 6) & 0x0000f800;
+ outputPacked |= (packed << 9) & 0x00f80000;
+
+ // Replicate 5 bit components.
+ outputPacked |= (outputPacked >> 5) & 0x00070707;
+
+ outputSpan[outOffset++] = outputPacked;
+ }
+
+ offset += remainder;
+ outOffset += outRemainder;
+ }
+
+ return output;
+ }
+
+ public unsafe static byte[] ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
+ {
+ byte[] output = new byte[data.Length * 2];
+ int offset = 0;
+ int outOffset = 0;
+
+ (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
+
+ ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
+ Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ uint packed = inputSpan[offset++];
+
+ uint a = packed >> 15;
+
+ uint outputPacked = a * 0xff000000;
+ outputPacked |= (packed >> 8) & 0x000000f8;
+ outputPacked |= (packed << 5) & 0x0000f800;
+ outputPacked |= (packed << 18) & 0x00f80000;
+
+ // Replicate 5 bit components.
+ outputPacked |= (outputPacked >> 5) & 0x00070707;
+
+ outputSpan[outOffset++] = outputPacked;
+ }
+
+ offset += remainder;
+ outOffset += outRemainder;
+ }
+
+ return output;
+ }
+
+ public unsafe static byte[] ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
+ {
+ byte[] output = new byte[data.Length * 2];
+ int offset = 0;
+ int outOffset = 0;
+
+ (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
+
+ ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
+ Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ uint packed = inputSpan[offset++];
+
+ uint outputPacked = packed & 0x0000000f;
+ outputPacked |= (packed << 4) & 0x00000f00;
+ outputPacked |= (packed << 8) & 0x000f0000;
+ outputPacked |= (packed << 12) & 0x0f000000;
+
+ outputSpan[outOffset++] = outputPacked * 0x11;
+ }
+
+ offset += remainder;
+ outOffset += outRemainder;
}
return output;