aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Image
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs21
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs10
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs42
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs35
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs3
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs180
6 files changed, 211 insertions, 80 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
index 8a9f37bb..b95c684e 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
@@ -8,6 +8,11 @@ namespace Ryujinx.Graphics.Gpu.Image
readonly struct FormatInfo
{
/// <summary>
+ /// An invalid texture format.
+ /// </summary>
+ public static FormatInfo Invalid { get; } = new(0, 0, 0, 0, 0);
+
+ /// <summary>
/// A default, generic RGBA8 texture format.
/// </summary>
public static FormatInfo Default { get; } = new(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
@@ -23,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <remarks>
/// Must be 1 for non-compressed formats.
/// </remarks>
- public int BlockWidth { get; }
+ public byte BlockWidth { get; }
/// <summary>
/// The block height for compressed formats.
@@ -31,17 +36,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <remarks>
/// Must be 1 for non-compressed formats.
/// </remarks>
- public int BlockHeight { get; }
+ public byte BlockHeight { get; }
/// <summary>
/// The number of bytes occupied by a single pixel in memory of the texture data.
/// </summary>
- public int BytesPerPixel { get; }
+ public byte BytesPerPixel { get; }
/// <summary>
/// The maximum number of components this format has defined (in RGBA order).
/// </summary>
- public int Components { get; }
+ public byte Components { get; }
/// <summary>
/// Whenever or not the texture format is a compressed format. Determined from block size.
@@ -57,10 +62,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
public FormatInfo(
Format format,
- int blockWidth,
- int blockHeight,
- int bytesPerPixel,
- int components)
+ byte blockWidth,
+ byte blockHeight,
+ byte bytesPerPixel,
+ byte components)
{
Format = format;
BlockWidth = blockWidth;
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
index 31abc21e..e9930405 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// For images, indicates the format specified on the shader.
/// </summary>
- public Format Format { get; }
+ public FormatInfo FormatInfo { get; }
/// <summary>
/// Shader texture host set index.
@@ -58,17 +58,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs the texture binding information structure.
/// </summary>
/// <param name="target">The shader sampler target type</param>
- /// <param name="format">Format of the image as declared on the shader</param>
+ /// <param name="formatInfo">Format of the image as declared on the shader</param>
/// <param name="set">Shader texture host set index</param>
/// <param name="binding">The shader texture binding point</param>
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
- public TextureBindingInfo(Target target, Format format, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
+ public TextureBindingInfo(Target target, FormatInfo formatInfo, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
{
Target = target;
- Format = format;
+ FormatInfo = formatInfo;
Set = set;
Binding = binding;
ArrayLength = arrayLength;
@@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int cbufSlot,
int handle,
TextureUsageFlags flags,
- bool isSamplerOnly) : this(target, 0, set, binding, arrayLength, cbufSlot, handle, flags)
+ bool isSamplerOnly) : this(target, FormatInfo.Invalid, set, binding, arrayLength, cbufSlot, handle, flags)
{
IsSamplerOnly = isSamplerOnly;
}
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
index 8b9243b1..72bac75e 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
@@ -659,7 +659,6 @@ namespace Ryujinx.Graphics.Gpu.Image
int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
length = Math.Min(length, bindingInfo.ArrayLength);
- Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
@@ -674,7 +673,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
- ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture);
+ ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
if (texture != null)
{
@@ -697,8 +696,6 @@ namespace Ryujinx.Graphics.Gpu.Image
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
ISampler hostSampler = sampler?.GetHostSampler(texture);
- Format format = bindingInfo.Format;
-
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
// Ensure that the buffer texture is using the correct buffer as storage.
@@ -706,26 +703,15 @@ namespace Ryujinx.Graphics.Gpu.Image
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
+ _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
}
else
{
- _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
+ _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
}
}
else if (isImage)
{
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- formats[index] = format;
textures[index] = hostTexture;
}
else
@@ -737,7 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image
if (isImage)
{
- entry.ImageArray.SetFormats(0, formats);
entry.ImageArray.SetImages(0, textures);
SetImageArray(stage, bindingInfo, entry.ImageArray);
@@ -863,7 +848,6 @@ namespace Ryujinx.Graphics.Gpu.Image
entry.UpdateData(cachedTextureBuffer, cachedSamplerBuffer, separateSamplerBuffer);
- Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
@@ -883,7 +867,7 @@ namespace Ryujinx.Graphics.Gpu.Image
samplerId = TextureHandle.UnpackSamplerId(packedId);
}
- ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
+ ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture);
if (texture != null)
{
@@ -916,8 +900,6 @@ namespace Ryujinx.Graphics.Gpu.Image
hostSampler = sampler?.GetHostSampler(texture);
}
- Format format = bindingInfo.Format;
-
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
// Ensure that the buffer texture is using the correct buffer as storage.
@@ -925,26 +907,15 @@ namespace Ryujinx.Graphics.Gpu.Image
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
+ _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
}
else
{
- _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
+ _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
}
}
else if (isImage)
{
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- formats[index] = format;
textures[index] = hostTexture;
}
else
@@ -956,7 +927,6 @@ namespace Ryujinx.Graphics.Gpu.Image
if (isImage)
{
- entry.ImageArray.SetFormats(0, formats);
entry.ImageArray.SetImages(0, textures);
SetImageArray(stage, bindingInfo, entry.ImageArray);
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index 9f1f60d9..ad018f15 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -522,7 +522,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// Ensure that the buffer texture is using the correct buffer as storage.
// 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.
- _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, bindingInfo.Format, false);
+ _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -616,6 +616,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!poolModified &&
state.TextureHandle == textureId &&
+ state.ImageFormat == bindingInfo.FormatInfo.Format &&
state.CachedTexture != null &&
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
{
@@ -629,26 +630,22 @@ namespace Ryujinx.Graphics.Gpu.Image
cachedTexture.SignalModified();
}
- Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
-
- if (state.ImageFormat != format ||
- ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
- UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage)))
+ if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
{
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
state.Texture = hostTextureRebind;
- state.ImageFormat = format;
- _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
+ _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind);
}
continue;
}
state.TextureHandle = textureId;
+ state.ImageFormat = bindingInfo.FormatInfo.Format;
- ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
+ ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture);
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
@@ -660,14 +657,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// 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.
- Format format = bindingInfo.Format;
-
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, format, true);
+ _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -689,16 +679,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
state.Texture = hostTexture;
- Format format = bindingInfo.Format;
-
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- state.ImageFormat = format;
-
- _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
+ _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture);
}
state.CachedTexture = texture;
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index 3cdeac9c..8bed6363 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -739,7 +739,8 @@ namespace Ryujinx.Graphics.Gpu.Image
}
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
- (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm);
+ (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm) ||
+ (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R32Uint);
}
/// <summary>
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()