Allow creating texture aliases on texture pool
This commit is contained in:
parent
2f36a6665c
commit
4d9575f76b
3 changed files with 300 additions and 7 deletions
|
@ -711,5 +711,120 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the bytes per pixel for a image compatible format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">Format</param>
|
||||||
|
/// <returns>Bytes per pixel, or zero if the format is not a image compatible format</returns>
|
||||||
|
public static int GetImageFormatBytesPerPixel(Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.R8Unorm:
|
||||||
|
case Format.R8Snorm:
|
||||||
|
case Format.R8Uint:
|
||||||
|
case Format.R8Sint:
|
||||||
|
return 1;
|
||||||
|
case Format.R16Float:
|
||||||
|
case Format.R16Unorm:
|
||||||
|
case Format.R16Snorm:
|
||||||
|
case Format.R16Uint:
|
||||||
|
case Format.R16Sint:
|
||||||
|
case Format.R8G8Unorm:
|
||||||
|
case Format.R8G8Snorm:
|
||||||
|
case Format.R8G8Uint:
|
||||||
|
case Format.R8G8Sint:
|
||||||
|
return 2;
|
||||||
|
case Format.R32Float:
|
||||||
|
case Format.R32Uint:
|
||||||
|
case Format.R32Sint:
|
||||||
|
case Format.R16G16Float:
|
||||||
|
case Format.R16G16Unorm:
|
||||||
|
case Format.R16G16Snorm:
|
||||||
|
case Format.R16G16Uint:
|
||||||
|
case Format.R16G16Sint:
|
||||||
|
case Format.R8G8B8A8Unorm:
|
||||||
|
case Format.R8G8B8A8Snorm:
|
||||||
|
case Format.R8G8B8A8Uint:
|
||||||
|
case Format.R8G8B8A8Sint:
|
||||||
|
case Format.R10G10B10A2Unorm:
|
||||||
|
case Format.R10G10B10A2Uint:
|
||||||
|
case Format.R11G11B10Float:
|
||||||
|
return 4;
|
||||||
|
case Format.R32G32Float:
|
||||||
|
case Format.R32G32Uint:
|
||||||
|
case Format.R32G32Sint:
|
||||||
|
case Format.R16G16B16A16Float:
|
||||||
|
case Format.R16G16B16A16Unorm:
|
||||||
|
case Format.R16G16B16A16Snorm:
|
||||||
|
case Format.R16G16B16A16Uint:
|
||||||
|
case Format.R16G16B16A16Sint:
|
||||||
|
return 8;
|
||||||
|
case Format.R32G32B32A32Float:
|
||||||
|
case Format.R32G32B32A32Uint:
|
||||||
|
case Format.R32G32B32A32Sint:
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the amount of components (RGBA) for a image compatible format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">Format</param>
|
||||||
|
/// <returns>Number of components (from 1 to 4), or zero if the format is not a image compatible format</returns>
|
||||||
|
public static int GetImageFormatComponents(Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.R8Unorm:
|
||||||
|
case Format.R8Snorm:
|
||||||
|
case Format.R8Uint:
|
||||||
|
case Format.R8Sint:
|
||||||
|
case Format.R16Float:
|
||||||
|
case Format.R16Unorm:
|
||||||
|
case Format.R16Snorm:
|
||||||
|
case Format.R16Uint:
|
||||||
|
case Format.R16Sint:
|
||||||
|
case Format.R32Float:
|
||||||
|
case Format.R32Uint:
|
||||||
|
case Format.R32Sint:
|
||||||
|
return 1;
|
||||||
|
case Format.R8G8Unorm:
|
||||||
|
case Format.R8G8Snorm:
|
||||||
|
case Format.R8G8Uint:
|
||||||
|
case Format.R8G8Sint:
|
||||||
|
case Format.R16G16Float:
|
||||||
|
case Format.R16G16Unorm:
|
||||||
|
case Format.R16G16Snorm:
|
||||||
|
case Format.R16G16Uint:
|
||||||
|
case Format.R16G16Sint:
|
||||||
|
case Format.R32G32Float:
|
||||||
|
case Format.R32G32Uint:
|
||||||
|
case Format.R32G32Sint:
|
||||||
|
return 2;
|
||||||
|
case Format.R11G11B10Float:
|
||||||
|
return 3;
|
||||||
|
case Format.R8G8B8A8Unorm:
|
||||||
|
case Format.R8G8B8A8Snorm:
|
||||||
|
case Format.R8G8B8A8Uint:
|
||||||
|
case Format.R8G8B8A8Sint:
|
||||||
|
case Format.R10G10B10A2Unorm:
|
||||||
|
case Format.R10G10B10A2Uint:
|
||||||
|
case Format.R16G16B16A16Float:
|
||||||
|
case Format.R16G16B16A16Unorm:
|
||||||
|
case Format.R16G16B16A16Snorm:
|
||||||
|
case Format.R16G16B16A16Uint:
|
||||||
|
case Format.R16G16B16A16Sint:
|
||||||
|
case Format.R32G32B32A32Float:
|
||||||
|
case Format.R32G32B32A32Uint:
|
||||||
|
case Format.R32G32B32A32Sint:
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -629,7 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
cachedTexture.SignalModified();
|
cachedTexture.SignalModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
Format format = cachedTexture.Format; // bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
||||||
|
|
||||||
if (state.ImageFormat != format ||
|
if (state.ImageFormat != format ||
|
||||||
((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||||
|
@ -648,7 +648,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
state.TextureHandle = textureId;
|
state.TextureHandle = textureId;
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
|
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, bindingInfo.Format, out Texture texture);
|
||||||
|
|
||||||
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
||||||
|
|
||||||
|
@ -660,7 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
Format format = 0; // bindingInfo.Format;
|
||||||
|
|
||||||
if (format == 0 && texture != null)
|
if (format == 0 && texture != null)
|
||||||
{
|
{
|
||||||
|
@ -689,7 +689,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
state.Texture = hostTexture;
|
state.Texture = hostTexture;
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
Format format = 0; // bindingInfo.Format;
|
||||||
|
|
||||||
if (format == 0 && texture != null)
|
if (format == 0 && texture != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -75,6 +75,76 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
|
private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
|
||||||
private TextureDescriptor _defaultDescriptor;
|
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>
|
/// <summary>
|
||||||
/// Linked list node used on the texture pool cache.
|
/// Linked list node used on the texture pool cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -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)
|
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -115,14 +186,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
|
||||||
|
|
||||||
// The dereference queue can put our texture back on the cache.
|
// The dereference queue can put our texture back on the cache.
|
||||||
if ((texture = ProcessDereferenceQueue(id)) != null)
|
if ((texture = ProcessDereferenceQueue(id)) != null)
|
||||||
{
|
{
|
||||||
return ref descriptor;
|
return ref descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, 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.
|
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
|
@ -197,6 +267,44 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return ref GetInternal(id, out texture);
|
return ref GetInternal(id, out texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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="texture">The texture with the given ID</param>
|
||||||
|
/// <returns>The texture descriptor with the given ID</returns>
|
||||||
|
public ref readonly TextureDescriptor GetForBinding(int id, Format format, out Texture texture)
|
||||||
|
{
|
||||||
|
ref readonly TextureDescriptor descriptor = ref GetInternal(id, out texture);
|
||||||
|
|
||||||
|
if (texture != null && format != 0 && texture.Format != format)
|
||||||
|
{
|
||||||
|
if (!_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
|
||||||
|
{
|
||||||
|
_aliasLists.Add(texture, aliasList = new TextureAliasList());
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = aliasList.Find(format);
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
|
info = ChangeFormat(info, format);
|
||||||
|
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
aliasList.Add(format, texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -234,6 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount();
|
texture.DecrementReferenceCount();
|
||||||
|
RemoveAliasList(texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +436,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount();
|
texture.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveAliasList(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -369,6 +480,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
if (Interlocked.Exchange(ref Items[id], null) != null)
|
if (Interlocked.Exchange(ref Items[id], null) != null)
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount(this, id);
|
texture.DecrementReferenceCount(this, id);
|
||||||
|
RemoveAliasList(texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,6 +734,68 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
component == SwizzleComponent.Green;
|
component == SwizzleComponent.Green;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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, Format dstFormat)
|
||||||
|
{
|
||||||
|
int dstBpp = FormatTable.GetImageFormatBytesPerPixel(dstFormat);
|
||||||
|
|
||||||
|
if (dstBpp == 0)
|
||||||
|
{
|
||||||
|
// We don't support the format. Should never happen in practice.
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatInfo dstFormatInfo = new FormatInfo(dstFormat, 1, 1, dstBpp, FormatTable.GetImageFormatComponents(dstFormat));
|
||||||
|
|
||||||
|
int width = info.Width;
|
||||||
|
|
||||||
|
if (info.FormatInfo.BytesPerPixel != dstBpp)
|
||||||
|
{
|
||||||
|
int stride = width * info.FormatInfo.BytesPerPixel;
|
||||||
|
width = stride / dstBpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
dstFormatInfo,
|
||||||
|
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>
|
/// <summary>
|
||||||
/// Decrements the reference count of the texture.
|
/// Decrements the reference count of the texture.
|
||||||
/// This indicates that the texture pool is not using it anymore.
|
/// This indicates that the texture pool is not using it anymore.
|
||||||
|
@ -629,7 +803,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="item">The texture to be deleted</param>
|
/// <param name="item">The texture to be deleted</param>
|
||||||
protected override void Delete(Texture item)
|
protected override void Delete(Texture item)
|
||||||
{
|
{
|
||||||
item?.DecrementReferenceCount(this);
|
if (item != null)
|
||||||
|
{
|
||||||
|
item.DecrementReferenceCount(this);
|
||||||
|
RemoveAliasList(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue