diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs index 1b517e63f4..0af0725a2f 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs @@ -651,9 +651,35 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if the format is valid, false otherwise public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format) { - encoded |= (isSrgb ? 1u << 19 : 0u); + bool isPacked = (encoded & 0x80000000u) != 0; + if (isPacked) + { + encoded &= ~0x80000000u; + } - return _textureFormats.TryGetValue((TextureFormat)encoded, out format); + encoded |= isSrgb ? 1u << 19 : 0u; + + bool found = _textureFormats.TryGetValue((TextureFormat)encoded, out format); + + if (found && isPacked && !format.Format.IsDepthOrStencil()) + { + // If the packed flag is set, then the components of the pixel are tightly packed into the + // GPU registers on the shader. + // We can get the same behaviour by aliasing the texture as a format with the same amount of + // bytes per pixel, but only a single or the lowest possible number of components. + + format = format.BytesPerPixel switch + { + 1 => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1), + 2 => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1), + 4 => new FormatInfo(Format.R32Float, 1, 1, 4, 1), + 8 => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2), + 16 => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4), + _ => format, + }; + } + + return found; } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 5af0471c02..eef38948dd 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -2,6 +2,8 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Texture; using System; +using System.Diagnostics; +using System.Numerics; namespace Ryujinx.Graphics.Gpu.Image { @@ -339,7 +341,20 @@ namespace Ryujinx.Graphics.Gpu.Image if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo)) { - alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel; + // If the formats are incompatible, but the texture strides match, + // we might allow them to be copy compatible depending on the format. + // The strides are aligned because the format with higher bytes per pixel + // might need a bit of padding at the end due to one width not being a multiple of the other. + + Debug.Assert((1 << BitOperations.Log2((uint)lhs.FormatInfo.BytesPerPixel)) == lhs.FormatInfo.BytesPerPixel); + Debug.Assert((1 << BitOperations.Log2((uint)rhs.FormatInfo.BytesPerPixel)) == rhs.FormatInfo.BytesPerPixel); + + int alignment = Math.Max(lhs.FormatInfo.BytesPerPixel, rhs.FormatInfo.BytesPerPixel); + + int lhsStride = BitUtils.AlignUp(lhsSize.Width * lhs.FormatInfo.BytesPerPixel, alignment); + int rhsStride = BitUtils.AlignUp(rhsSize.Width * rhs.FormatInfo.BytesPerPixel, alignment); + + alignedWidthMatches = lhsStride == rhsStride; } TextureViewCompatibility result = TextureViewCompatibility.Full; @@ -718,7 +733,8 @@ namespace Ryujinx.Graphics.Gpu.Image (lhsFormat, rhsFormat) = (rhsFormat, lhsFormat); } - return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm; + return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) || + (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 0fdb6cd649..a4035577d3 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -430,7 +430,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo)) { - if (gpuVa != 0 && (int)format > 0) + if (gpuVa != 0 && format != 0) { Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb})."); }