From cda659955ced1b16839cdd1e7fea1ef6f8d99041 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 9 Jan 2022 16:28:48 +0000 Subject: Texture Sync, incompatible overlap handling, data flush improvements. (#2971) * Initial test for texture sync * WIP new texture flushing setup * Improve rules for incompatible overlaps Fixes a lot of issues with Unreal Engine games. Still a few minor issues (some caused by dma fast path?) Needs docs and cleanup. * Cleanup, improvements Improve rules for fast DMA * Small tweak to group together flushes of overlapping handles. * Fixes, flush overlapping texture data for ASTC and BC4/5 compressed textures. Fixes the new Life is Strange game. * Flush overlaps before init data, fix 3d texture size/overlap stuff * Fix 3D Textures, faster single layer flush Note: nosy people can no longer merge this with Vulkan. (unless they are nosy enough to implement the new backend methods) * Remove unused method * Minor cleanup * More cleanup * Use the More Fun and Hopefully No Driver Bugs method for getting compressed tex too This one's for metro * Address feedback, ASTC+ETC to FormatClass * Change offset to use Span slice rather than IntPtr Add * Fix this too --- Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs | 219 +++++++++++++++++++-- 1 file changed, 201 insertions(+), 18 deletions(-) (limited to 'Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs') diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index ce9fd75c..0461a81f 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Texture; using System; +using System.Numerics; namespace Ryujinx.Graphics.Gpu.Image { @@ -22,7 +23,23 @@ namespace Ryujinx.Graphics.Gpu.Image Bc4, Bc5, Bc6, - Bc7 + Bc7, + Etc2Rgb, + Etc2Rgba, + Astc4x4, + Astc5x4, + Astc5x5, + Astc6x5, + Astc6x6, + Astc8x5, + Astc8x6, + Astc8x8, + Astc10x5, + Astc10x6, + Astc10x8, + Astc10x10, + Astc12x10, + Astc12x12 } /// @@ -92,34 +109,60 @@ namespace Ryujinx.Graphics.Gpu.Image return info.FormatInfo; } + /// + /// Determines whether a texture can flush its data back to guest memory. + /// + /// Texture information + /// Host GPU Capabilities + /// True if the texture can flush, false otherwise + public static bool CanTextureFlush(TextureInfo info, Capabilities caps) + { + if (IsFormatHostIncompatible(info, caps)) + { + return false; // Flushing this format is not supported, as it may have been converted to another host format. + } + + if (info.Target == Target.Texture2DMultisample || + info.Target == Target.Texture2DMultisampleArray) + { + return false; // Flushing multisample textures is not supported, the host does not allow getting their data. + } + + return true; + } + /// /// Checks if two formats are compatible, according to the host API copy format compatibility rules. /// - /// First comparand - /// Second comparand + /// First comparand + /// Second comparand + /// Host GPU capabilities /// True if the formats are compatible, false otherwise - public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs) + public static bool FormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) { - if (lhs.Format.IsDepthOrStencil() || rhs.Format.IsDepthOrStencil()) + FormatInfo lhsFormat = lhs.FormatInfo; + FormatInfo rhsFormat = rhs.FormatInfo; + + if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) { - return lhs.Format == rhs.Format; + return lhsFormat.Format == rhsFormat.Format; } - if (lhs.Format.IsAstc() || rhs.Format.IsAstc()) + if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps)) { - return lhs.Format == rhs.Format; + return lhsFormat.Format == rhsFormat.Format; } - if (lhs.IsCompressed && rhs.IsCompressed) + if (lhsFormat.IsCompressed && rhsFormat.IsCompressed) { - FormatClass lhsClass = GetFormatClass(lhs.Format); - FormatClass rhsClass = GetFormatClass(rhs.Format); + FormatClass lhsClass = GetFormatClass(lhsFormat.Format); + FormatClass rhsClass = GetFormatClass(rhsFormat.Format); return lhsClass == rhsClass; } else { - return lhs.BytesPerPixel == rhs.BytesPerPixel; + return lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel; } } @@ -204,6 +247,10 @@ namespace Ryujinx.Graphics.Gpu.Image { return TextureViewCompatibility.Incompatible; } + else if (first == TextureViewCompatibility.LayoutIncompatible || second == TextureViewCompatibility.LayoutIncompatible) + { + return TextureViewCompatibility.LayoutIncompatible; + } else if (first == TextureViewCompatibility.CopyOnly || second == TextureViewCompatibility.CopyOnly) { return TextureViewCompatibility.CopyOnly; @@ -214,6 +261,37 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Checks if the sizes of two texture levels are copy compatible. + /// + /// Texture information of the texture view + /// Texture information of the texture view to match against + /// Mipmap level of the texture view in relation to this texture + /// Mipmap level of the texture view in relation to the second texture + /// True if both levels are view compatible + public static bool CopySizeMatches(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel) + { + Size size = GetAlignedSize(lhs, lhsLevel); + + Size otherSize = GetAlignedSize(rhs, rhsLevel); + + if (size.Width == otherSize.Width && size.Height == otherSize.Height) + { + return true; + } + else if (lhs.IsLinear && rhs.IsLinear) + { + // Copy between linear textures with matching stride. + int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> lhsLevel), Constants.StrideAlignment); + + return stride == rhs.Stride; + } + else + { + return false; + } + } + /// /// Checks if the sizes of two given textures are view compatible. /// @@ -259,11 +337,11 @@ namespace Ryujinx.Graphics.Gpu.Image // Copy between linear textures with matching stride. int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment); - return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.Incompatible; + return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible; } else { - return TextureViewCompatibility.Incompatible; + return TextureViewCompatibility.LayoutIncompatible; } } @@ -284,7 +362,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - return TextureViewCompatibility.Incompatible; + return TextureViewCompatibility.LayoutIncompatible; } } @@ -435,7 +513,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (rhs.IsLinear) { int stride = Math.Max(1, lhs.Stride >> level); - stride = BitUtils.AlignUp(stride, 32); + stride = BitUtils.AlignUp(stride, Constants.StrideAlignment); return stride == rhs.Stride; } @@ -456,6 +534,62 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Check if it's possible to create a view with the layout of the second texture information from the first. + /// The layout information is composed of the Stride for linear textures, or GOB block size + /// for block linear textures. + /// + /// Texture information of the texture view + /// Texture information of the texture view to compare against + /// Start level of the texture view, in relation with the first texture + /// Start level of the texture view, in relation with the second texture + /// True if the layout is compatible, false otherwise + public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel) + { + if (lhs.IsLinear != rhs.IsLinear) + { + return false; + } + + // For linear textures, gob block sizes are ignored. + // For block linear textures, the stride is ignored. + if (rhs.IsLinear) + { + int lhsStride = Math.Max(1, lhs.Stride >> lhsLevel); + lhsStride = BitUtils.AlignUp(lhsStride, Constants.StrideAlignment); + + int rhsStride = Math.Max(1, rhs.Stride >> rhsLevel); + rhsStride = BitUtils.AlignUp(rhsStride, Constants.StrideAlignment); + + return lhsStride == rhsStride; + } + else + { + int lhsHeight = Math.Max(1, lhs.Height >> lhsLevel); + int lhsDepth = Math.Max(1, lhs.GetDepth() >> lhsLevel); + + (int lhsGobBlocksInY, int lhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( + lhsHeight, + lhsDepth, + lhs.FormatInfo.BlockHeight, + lhs.GobBlocksInY, + lhs.GobBlocksInZ); + + int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel); + int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel); + + (int rhsGobBlocksInY, int rhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( + rhsHeight, + rhsDepth, + rhs.FormatInfo.BlockHeight, + rhs.GobBlocksInY, + rhs.GobBlocksInZ); + + return lhsGobBlocksInY == rhsGobBlocksInY && + lhsGobBlocksInZ == rhsGobBlocksInZ; + } + } + /// /// Checks if the view format of the first texture format is compatible with the format of the second. /// In general, the formats are considered compatible if the bytes per pixel values are equal, @@ -464,10 +598,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Texture information of the texture view /// Texture information of the texture view + /// Host GPU capabilities /// The view compatibility level of the texture formats - public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs) + public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) { - if (FormatCompatible(lhs.FormatInfo, rhs.FormatInfo)) + if (FormatCompatible(lhs, rhs, caps)) { if (lhs.FormatInfo.IsCompressed != rhs.FormatInfo.IsCompressed) { @@ -638,6 +773,54 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.Bc7Srgb: case Format.Bc7Unorm: return FormatClass.Bc7; + case Format.Etc2RgbSrgb: + case Format.Etc2RgbUnorm: + return FormatClass.Etc2Rgb; + case Format.Etc2RgbaSrgb: + case Format.Etc2RgbaUnorm: + return FormatClass.Etc2Rgba; + case Format.Astc4x4Srgb: + case Format.Astc4x4Unorm: + return FormatClass.Astc4x4; + case Format.Astc5x4Srgb: + case Format.Astc5x4Unorm: + return FormatClass.Astc5x4; + case Format.Astc5x5Srgb: + case Format.Astc5x5Unorm: + return FormatClass.Astc5x5; + case Format.Astc6x5Srgb: + case Format.Astc6x5Unorm: + return FormatClass.Astc6x5; + case Format.Astc6x6Srgb: + case Format.Astc6x6Unorm: + return FormatClass.Astc6x6; + case Format.Astc8x5Srgb: + case Format.Astc8x5Unorm: + return FormatClass.Astc8x5; + case Format.Astc8x6Srgb: + case Format.Astc8x6Unorm: + return FormatClass.Astc8x6; + case Format.Astc8x8Srgb: + case Format.Astc8x8Unorm: + return FormatClass.Astc8x8; + case Format.Astc10x5Srgb: + case Format.Astc10x5Unorm: + return FormatClass.Astc10x5; + case Format.Astc10x6Srgb: + case Format.Astc10x6Unorm: + return FormatClass.Astc10x6; + case Format.Astc10x8Srgb: + case Format.Astc10x8Unorm: + return FormatClass.Astc10x8; + case Format.Astc10x10Srgb: + case Format.Astc10x10Unorm: + return FormatClass.Astc10x10; + case Format.Astc12x10Srgb: + case Format.Astc12x10Unorm: + return FormatClass.Astc12x10; + case Format.Astc12x12Srgb: + case Format.Astc12x12Unorm: + return FormatClass.Astc12x12; } return FormatClass.Unclassified; -- cgit v1.2.3