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
This commit is contained in:
riperiperi 2022-01-09 16:28:48 +00:00 committed by GitHub
parent 4864648e72
commit cda659955c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1453 additions and 329 deletions

View file

@ -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>
@ -92,34 +109,60 @@ namespace Ryujinx.Graphics.Gpu.Image
return info.FormatInfo;
}
/// <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;
@ -214,6 +261,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>
@ -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
}
}
/// <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,
@ -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;