aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-09-17 15:52:30 -0300
committerGitHub <noreply@github.com>2024-09-17 15:52:30 -0300
commiteb8132b627d3c0285dd199f4e40c6f3800bdb22d (patch)
tree970a119909a0dd901bbd8cb5454b725a08e329b3 /src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
parentccf96bf5e673456ec80f72725e4c9afa4e4c5a85 (diff)
Change image format view handling to allow view incompatible formats (#7311)
* Allow creating texture aliases on texture pool * Delete old image format override code * New format incompatible alias * Missing bounds check * GetForBinding now takes FormatInfo * Make FormatInfo struct more compact
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs180
1 files changed, 177 insertions, 3 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index 4ed0a93c..5f43c182 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -76,6 +76,76 @@ namespace Ryujinx.Graphics.Gpu.Image
private TextureDescriptor _defaultDescriptor;
/// <summary>
+ /// List of textures that shares the same memory region, but have different formats.
+ /// </summary>
+ private class TextureAliasList
+ {
+ /// <summary>
+ /// Alias texture.
+ /// </summary>
+ /// <param name="Format">Texture format</param>
+ /// <param name="Texture">Texture</param>
+ private readonly record struct Alias(Format Format, Texture Texture);
+
+ /// <summary>
+ /// List of texture aliases.
+ /// </summary>
+ private readonly List<Alias> _aliases;
+
+ /// <summary>
+ /// Creates a new instance of the texture alias list.
+ /// </summary>
+ public TextureAliasList()
+ {
+ _aliases = new List<Alias>();
+ }
+
+ /// <summary>
+ /// Adds a new texture alias.
+ /// </summary>
+ /// <param name="format">Alias format</param>
+ /// <param name="texture">Alias texture</param>
+ public void Add(Format format, Texture texture)
+ {
+ _aliases.Add(new Alias(format, texture));
+ texture.IncrementReferenceCount();
+ }
+
+ /// <summary>
+ /// Finds a texture with the requested format, or returns null if not found.
+ /// </summary>
+ /// <param name="format">Format to find</param>
+ /// <returns>Texture with the requested format, or null if not found</returns>
+ public Texture Find(Format format)
+ {
+ foreach (var alias in _aliases)
+ {
+ if (alias.Format == format)
+ {
+ return alias.Texture;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Removes all alias textures.
+ /// </summary>
+ public void Destroy()
+ {
+ foreach (var entry in _aliases)
+ {
+ entry.Texture.DecrementReferenceCount();
+ }
+
+ _aliases.Clear();
+ }
+ }
+
+ private readonly Dictionary<Texture, TextureAliasList> _aliasLists;
+
+ /// <summary>
/// Linked list node used on the texture pool cache.
/// </summary>
public LinkedListNode<TexturePool> CacheNode { get; set; }
@@ -95,6 +165,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
{
_channel = channel;
+ _aliasLists = new Dictionary<Texture, TextureAliasList>();
}
/// <summary>
@@ -115,14 +186,13 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture == null)
{
- TextureInfo info = GetInfo(descriptor, out int layerSize);
-
// The dereference queue can put our texture back on the cache.
if ((texture = ProcessDereferenceQueue(id)) != null)
{
return ref descriptor;
}
+ TextureInfo info = GetInfo(descriptor, out int layerSize);
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
// If this happens, then the texture address is invalid, we can't add it to the cache.
@@ -198,6 +268,51 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Gets the texture descriptor and texture with the given ID.
+ /// </summary>
+ /// <remarks>
+ /// This method assumes that the pool has been manually synchronized before doing binding.
+ /// </remarks>
+ /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
+ /// <param name="formatInfo">Texture format information</param>
+ /// <param name="texture">The texture with the given ID</param>
+ /// <returns>The texture descriptor with the given ID</returns>
+ public ref readonly TextureDescriptor GetForBinding(int id, FormatInfo formatInfo, out Texture texture)
+ {
+ if ((uint)id >= Items.Length)
+ {
+ texture = null;
+ return ref _defaultDescriptor;
+ }
+
+ ref readonly TextureDescriptor descriptor = ref GetInternal(id, out texture);
+
+ if (texture != null && formatInfo.Format != 0 && texture.Format != formatInfo.Format)
+ {
+ if (!_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
+ {
+ _aliasLists.Add(texture, aliasList = new TextureAliasList());
+ }
+
+ texture = aliasList.Find(formatInfo.Format);
+
+ if (texture == null)
+ {
+ TextureInfo info = GetInfo(descriptor, out int layerSize);
+ info = ChangeFormat(info, formatInfo);
+ texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
+
+ if (texture != null)
+ {
+ aliasList.Add(formatInfo.Format, texture);
+ }
+ }
+ }
+
+ return ref descriptor;
+ }
+
+ /// <summary>
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
/// </summary>
/// <returns>A number that increments each time a modification is detected</returns>
@@ -234,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image
else
{
texture.DecrementReferenceCount();
+ RemoveAliasList(texture);
}
}
@@ -327,6 +443,8 @@ namespace Ryujinx.Graphics.Gpu.Image
{
texture.DecrementReferenceCount();
}
+
+ RemoveAliasList(texture);
}
return null;
@@ -369,6 +487,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (Interlocked.Exchange(ref Items[id], null) != null)
{
texture.DecrementReferenceCount(this, id);
+ RemoveAliasList(texture);
}
}
}
@@ -623,13 +742,68 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Changes the format on the texture information structure, and also adjusts the width for the new format if needed.
+ /// </summary>
+ /// <param name="info">Texture information</param>
+ /// <param name="dstFormat">New format</param>
+ /// <returns>Texture information with the new format</returns>
+ private static TextureInfo ChangeFormat(in TextureInfo info, FormatInfo dstFormat)
+ {
+ int width = info.Width;
+
+ if (info.FormatInfo.BytesPerPixel != dstFormat.BytesPerPixel)
+ {
+ int stride = width * info.FormatInfo.BytesPerPixel;
+ width = stride / dstFormat.BytesPerPixel;
+ }
+
+ return new TextureInfo(
+ info.GpuAddress,
+ width,
+ info.Height,
+ info.DepthOrLayers,
+ info.Levels,
+ info.SamplesInX,
+ info.SamplesInY,
+ info.Stride,
+ info.IsLinear,
+ info.GobBlocksInY,
+ info.GobBlocksInZ,
+ info.GobBlocksInTileX,
+ info.Target,
+ dstFormat,
+ info.DepthStencilMode,
+ info.SwizzleR,
+ info.SwizzleG,
+ info.SwizzleB,
+ info.SwizzleA);
+ }
+
+ /// <summary>
+ /// Removes all aliases for a texture.
+ /// </summary>
+ /// <param name="texture">Texture to have the aliases removed</param>
+ private void RemoveAliasList(Texture texture)
+ {
+ if (_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
+ {
+ _aliasLists.Remove(texture);
+ aliasList.Destroy();
+ }
+ }
+
+ /// <summary>
/// Decrements the reference count of the texture.
/// This indicates that the texture pool is not using it anymore.
/// </summary>
/// <param name="item">The texture to be deleted</param>
protected override void Delete(Texture item)
{
- item?.DecrementReferenceCount(this);
+ if (item != null)
+ {
+ item.DecrementReferenceCount(this);
+ RemoveAliasList(item);
+ }
}
public override void Dispose()