From 79092310fa5510f3b8e9f1e0183de6dea9dad98a Mon Sep 17 00:00:00 2001 From: riperiperi Date: Mon, 24 May 2021 08:35:26 +0100 Subject: [PATCH] Compare aligned size for largest mip level when considering sampler resize (#2306) * Compare aligned size for largest mip level when considering sampler resize When selecting a texture that's a view for a sampler resize, we should take care that resizing it doesn't change the aligned size of any larger mip levels. This PR covers two cases: - When creating a view of the texture, we check that the aligned size of the view shifted up to level 0 still matches the aligned size of the container. If it does not, a copy dependency is created rather than resizing. - When searching for a texture for sampler, textures that do _not_ match our aligned size when both are shifted up by its base level are not considered an exact match, as resizing the found texture will cause the mip 0 aligned size to change. It will create a copy dependency view instead. Fixes graphical errors and crashes (on flush) in various Unity games that use render-to-texture. * Move shared code to its own method. --- Ryujinx.Graphics.Gpu/Image/Texture.cs | 2 +- .../Image/TextureCompatibility.cs | 66 +++++++++++++++---- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 4d7e31c570..6f720f4c18 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -941,7 +941,7 @@ namespace Ryujinx.Graphics.Gpu.Image return TextureMatchQuality.NoMatch; } - if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0)) + if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0, FirstLevel)) { return TextureMatchQuality.NoMatch; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 3140f4a125..c70b297193 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -233,6 +233,20 @@ namespace Ryujinx.Graphics.Gpu.Image if (size.Width == otherSize.Width && size.Height == otherSize.Height) { + if (level > 0 && result == TextureViewCompatibility.Full) + { + // A resize should not change the aligned size of the largest mip. + // If it would, then create a copy dependency rather than a full view. + + Size mip0SizeLhs = GetAlignedSize(lhs); + Size mip0SizeRhs = GetLargestAlignedSize(rhs, level); + + if (mip0SizeLhs.Width != mip0SizeRhs.Width || mip0SizeLhs.Height != mip0SizeRhs.Height) + { + result = TextureViewCompatibility.CopyOnly; + } + } + return result; } else if (lhs.IsLinear && rhs.IsLinear) @@ -300,8 +314,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// Texture information to compare /// Texture information to compare with /// True to align the sizes according to the texture layout for comparison + /// Mip level of the lhs texture. Aligned sizes are compared for the largest mip /// True if the sizes matches, false otherwise - public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes) + public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes, int lhsLevel = 0) { if (lhs.GetLayers() != rhs.GetLayers()) { @@ -312,8 +327,8 @@ namespace Ryujinx.Graphics.Gpu.Image if (alignSizes && !isTextureBuffer) { - Size size0 = GetAlignedSize(lhs); - Size size1 = GetAlignedSize(rhs); + Size size0 = GetLargestAlignedSize(lhs, lhsLevel); + Size size1 = GetLargestAlignedSize(rhs, lhsLevel); return size0.Width == size1.Width && size0.Height == size1.Height && @@ -328,17 +343,16 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Gets the aligned sizes of the specified texture information. + /// Gets the aligned sizes for the given dimensions, using the specified texture information. /// The alignment depends on the texture layout and format bytes per pixel. /// /// Texture information to calculate the aligned size from - /// Mipmap level for texture views + /// The width to be aligned + /// The height to be aligned + /// The depth to be aligned /// The aligned texture size - public static Size GetAlignedSize(TextureInfo info, int level = 0) + private static Size GetAlignedSize(TextureInfo info, int width, int height, int depth) { - int width = Math.Max(1, info.Width >> level); - int height = Math.Max(1, info.Height >> level); - if (info.IsLinear) { return SizeCalculator.GetLinearAlignedSize( @@ -350,8 +364,6 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - int depth = Math.Max(1, info.GetDepth() >> level); - return SizeCalculator.GetBlockLinearAlignedSize( width, height, @@ -365,6 +377,38 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Gets the aligned sizes of the specified texture information, shifted to the largest mip from a given level. + /// The alignment depends on the texture layout and format bytes per pixel. + /// + /// Texture information to calculate the aligned size from + /// Mipmap level for texture views. Shifts the aligned size to represent the largest mip level + /// The aligned texture size of the largest mip level + public static Size GetLargestAlignedSize(TextureInfo info, int level) + { + int width = info.Width << level; + int height = info.Height << level; + int depth = info.GetDepth() << level; + + return GetAlignedSize(info, width, height, depth); + } + + /// + /// Gets the aligned sizes of the specified texture information. + /// The alignment depends on the texture layout and format bytes per pixel. + /// + /// Texture information to calculate the aligned size from + /// Mipmap level for texture views + /// The aligned texture size + public static Size GetAlignedSize(TextureInfo info, int level = 0) + { + int width = Math.Max(1, info.Width >> level); + int height = Math.Max(1, info.Height >> level); + int depth = Math.Max(1, info.GetDepth() >> level); + + return GetAlignedSize(info, width, height, depth); + } + /// /// 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