aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2022-01-09 16:28:48 +0000
committerGitHub <noreply@github.com>2022-01-09 13:28:48 -0300
commitcda659955ced1b16839cdd1e7fea1ef6f8d99041 (patch)
tree41d0d7a705f9feb7b43c9e5bac14dafee7878220 /Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
parent4864648e727c6f526e3b65478f222c15468f6074 (diff)
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
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs219
1 files changed, 201 insertions, 18 deletions
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
}
/// <summary>
@@ -93,33 +110,59 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Determines whether a texture can flush its data back to guest memory.
+ /// </summary>
+ /// <param name="info">Texture information</param>
+ /// <param name="caps">Host GPU Capabilities</param>
+ /// <returns>True if the texture can flush, false otherwise</returns>
+ 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;
+ }
+
+ /// <summary>
/// Checks if two formats are compatible, according to the host API copy format compatibility rules.
/// </summary>
- /// <param name="lhs">First comparand</param>
- /// <param name="rhs">Second comparand</param>
+ /// <param name="lhsFormat">First comparand</param>
+ /// <param name="rhsFormat">Second comparand</param>
+ /// <param name="caps">Host GPU capabilities</param>
/// <returns>True if the formats are compatible, false otherwise</returns>
- 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;
@@ -215,6 +262,37 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Checks if the sizes of two texture levels are copy compatible.
+ /// </summary>
+ /// <param name="lhs">Texture information of the texture view</param>
+ /// <param name="rhs">Texture information of the texture view to match against</param>
+ /// <param name="lhsLevel">Mipmap level of the texture view in relation to this texture</param>
+ /// <param name="rhsLevel">Mipmap level of the texture view in relation to the second texture</param>
+ /// <returns>True if both levels are view compatible</returns>
+ 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;
+ }
+ }
+
+ /// <summary>
/// Checks if the sizes of two given textures are view compatible.
/// </summary>
/// <param name="lhs">Texture information of the texture view</param>
@@ -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;
}
@@ -457,6 +535,62 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="lhs">Texture information of the texture view</param>
+ /// <param name="rhs">Texture information of the texture view to compare against</param>
+ /// <param name="lhsLevel">Start level of the texture view, in relation with the first texture</param>
+ /// <param name="rhsLevel">Start level of the texture view, in relation with the second texture</param>
+ /// <returns>True if the layout is compatible, false otherwise</returns>
+ 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;
+ }
+ }
+
+ /// <summary>
/// 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,
/// but there are more complex rules for some formats, like compressed or depth-stencil formats.
@@ -464,10 +598,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="lhs">Texture information of the texture view</param>
/// <param name="rhs">Texture information of the texture view</param>
+ /// <param name="caps">Host GPU capabilities</param>
/// <returns>The view compatibility level of the texture formats</returns>
- 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;