From eb8132b627d3c0285dd199f4e40c6f3800bdb22d Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 17 Sep 2024 15:52:30 -0300 Subject: 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 --- src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs | 83 +++++----- src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs | 21 ++- .../Image/TextureBindingInfo.cs | 10 +- .../Image/TextureBindingsArrayCache.cs | 42 +---- .../Image/TextureBindingsManager.cs | 35 +--- .../Image/TextureCompatibility.cs | 3 +- src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 180 ++++++++++++++++++++- src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 15 +- .../Memory/BufferTextureArrayBinding.cs | 11 +- .../Memory/BufferTextureBinding.cs | 8 - .../Shader/CachedShaderBindings.cs | 4 +- src/Ryujinx.Graphics.Gpu/Window.cs | 2 +- 12 files changed, 264 insertions(+), 150 deletions(-) (limited to 'src/Ryujinx.Graphics.Gpu') diff --git a/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs index 7bff1c4b..bdb34180 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; namespace Ryujinx.Graphics.Gpu.Engine @@ -61,51 +62,51 @@ namespace Ryujinx.Graphics.Gpu.Engine /// /// Shader image format /// Texture format - public static Format GetFormat(TextureFormat format) + public static FormatInfo GetFormatInfo(TextureFormat format) { return format switch { #pragma warning disable IDE0055 // Disable formatting - TextureFormat.R8Unorm => Format.R8Unorm, - TextureFormat.R8Snorm => Format.R8Snorm, - TextureFormat.R8Uint => Format.R8Uint, - TextureFormat.R8Sint => Format.R8Sint, - TextureFormat.R16Float => Format.R16Float, - TextureFormat.R16Unorm => Format.R16Unorm, - TextureFormat.R16Snorm => Format.R16Snorm, - TextureFormat.R16Uint => Format.R16Uint, - TextureFormat.R16Sint => Format.R16Sint, - TextureFormat.R32Float => Format.R32Float, - TextureFormat.R32Uint => Format.R32Uint, - TextureFormat.R32Sint => Format.R32Sint, - TextureFormat.R8G8Unorm => Format.R8G8Unorm, - TextureFormat.R8G8Snorm => Format.R8G8Snorm, - TextureFormat.R8G8Uint => Format.R8G8Uint, - TextureFormat.R8G8Sint => Format.R8G8Sint, - TextureFormat.R16G16Float => Format.R16G16Float, - TextureFormat.R16G16Unorm => Format.R16G16Unorm, - TextureFormat.R16G16Snorm => Format.R16G16Snorm, - TextureFormat.R16G16Uint => Format.R16G16Uint, - TextureFormat.R16G16Sint => Format.R16G16Sint, - TextureFormat.R32G32Float => Format.R32G32Float, - TextureFormat.R32G32Uint => Format.R32G32Uint, - TextureFormat.R32G32Sint => Format.R32G32Sint, - TextureFormat.R8G8B8A8Unorm => Format.R8G8B8A8Unorm, - TextureFormat.R8G8B8A8Snorm => Format.R8G8B8A8Snorm, - TextureFormat.R8G8B8A8Uint => Format.R8G8B8A8Uint, - TextureFormat.R8G8B8A8Sint => Format.R8G8B8A8Sint, - TextureFormat.R16G16B16A16Float => Format.R16G16B16A16Float, - TextureFormat.R16G16B16A16Unorm => Format.R16G16B16A16Unorm, - TextureFormat.R16G16B16A16Snorm => Format.R16G16B16A16Snorm, - TextureFormat.R16G16B16A16Uint => Format.R16G16B16A16Uint, - TextureFormat.R16G16B16A16Sint => Format.R16G16B16A16Sint, - TextureFormat.R32G32B32A32Float => Format.R32G32B32A32Float, - TextureFormat.R32G32B32A32Uint => Format.R32G32B32A32Uint, - TextureFormat.R32G32B32A32Sint => Format.R32G32B32A32Sint, - TextureFormat.R10G10B10A2Unorm => Format.R10G10B10A2Unorm, - TextureFormat.R10G10B10A2Uint => Format.R10G10B10A2Uint, - TextureFormat.R11G11B10Float => Format.R11G11B10Float, - _ => 0, + TextureFormat.R8Unorm => new(Format.R8Unorm, 1, 1, 1, 1), + TextureFormat.R8Snorm => new(Format.R8Snorm, 1, 1, 1, 1), + TextureFormat.R8Uint => new(Format.R8Uint, 1, 1, 1, 1), + TextureFormat.R8Sint => new(Format.R8Sint, 1, 1, 1, 1), + TextureFormat.R16Float => new(Format.R16Float, 1, 1, 2, 1), + TextureFormat.R16Unorm => new(Format.R16Unorm, 1, 1, 2, 1), + TextureFormat.R16Snorm => new(Format.R16Snorm, 1, 1, 2, 1), + TextureFormat.R16Uint => new(Format.R16Uint, 1, 1, 2, 1), + TextureFormat.R16Sint => new(Format.R16Sint, 1, 1, 2, 1), + TextureFormat.R32Float => new(Format.R32Float, 1, 1, 4, 1), + TextureFormat.R32Uint => new(Format.R32Uint, 1, 1, 4, 1), + TextureFormat.R32Sint => new(Format.R32Sint, 1, 1, 4, 1), + TextureFormat.R8G8Unorm => new(Format.R8G8Unorm, 1, 1, 2, 2), + TextureFormat.R8G8Snorm => new(Format.R8G8Snorm, 1, 1, 2, 2), + TextureFormat.R8G8Uint => new(Format.R8G8Uint, 1, 1, 2, 2), + TextureFormat.R8G8Sint => new(Format.R8G8Sint, 1, 1, 2, 2), + TextureFormat.R16G16Float => new(Format.R16G16Float, 1, 1, 4, 2), + TextureFormat.R16G16Unorm => new(Format.R16G16Unorm, 1, 1, 4, 2), + TextureFormat.R16G16Snorm => new(Format.R16G16Snorm, 1, 1, 4, 2), + TextureFormat.R16G16Uint => new(Format.R16G16Uint, 1, 1, 4, 2), + TextureFormat.R16G16Sint => new(Format.R16G16Sint, 1, 1, 4, 2), + TextureFormat.R32G32Float => new(Format.R32G32Float, 1, 1, 8, 2), + TextureFormat.R32G32Uint => new(Format.R32G32Uint, 1, 1, 8, 2), + TextureFormat.R32G32Sint => new(Format.R32G32Sint, 1, 1, 8, 2), + TextureFormat.R8G8B8A8Unorm => new(Format.R8G8B8A8Unorm, 1, 1, 4, 4), + TextureFormat.R8G8B8A8Snorm => new(Format.R8G8B8A8Snorm, 1, 1, 4, 4), + TextureFormat.R8G8B8A8Uint => new(Format.R8G8B8A8Uint, 1, 1, 4, 4), + TextureFormat.R8G8B8A8Sint => new(Format.R8G8B8A8Sint, 1, 1, 4, 4), + TextureFormat.R16G16B16A16Float => new(Format.R16G16B16A16Float, 1, 1, 8, 4), + TextureFormat.R16G16B16A16Unorm => new(Format.R16G16B16A16Unorm, 1, 1, 8, 4), + TextureFormat.R16G16B16A16Snorm => new(Format.R16G16B16A16Snorm, 1, 1, 8, 4), + TextureFormat.R16G16B16A16Uint => new(Format.R16G16B16A16Uint, 1, 1, 8, 4), + TextureFormat.R16G16B16A16Sint => new(Format.R16G16B16A16Sint, 1, 1, 8, 4), + TextureFormat.R32G32B32A32Float => new(Format.R32G32B32A32Float, 1, 1, 16, 4), + TextureFormat.R32G32B32A32Uint => new(Format.R32G32B32A32Uint, 1, 1, 16, 4), + TextureFormat.R32G32B32A32Sint => new(Format.R32G32B32A32Sint, 1, 1, 16, 4), + TextureFormat.R10G10B10A2Unorm => new(Format.R10G10B10A2Unorm, 1, 1, 4, 4), + TextureFormat.R10G10B10A2Uint => new(Format.R10G10B10A2Uint, 1, 1, 4, 4), + TextureFormat.R11G11B10Float => new(Format.R11G11B10Float, 1, 1, 4, 3), + _ => FormatInfo.Invalid, #pragma warning restore IDE0055 }; } 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 @@ -7,6 +7,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// readonly struct FormatInfo { + /// + /// An invalid texture format. + /// + public static FormatInfo Invalid { get; } = new(0, 0, 0, 0, 0); + /// /// A default, generic RGBA8 texture format. /// @@ -23,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Must be 1 for non-compressed formats. /// - public int BlockWidth { get; } + public byte BlockWidth { get; } /// /// The block height for compressed formats. @@ -31,17 +36,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Must be 1 for non-compressed formats. /// - public int BlockHeight { get; } + public byte BlockHeight { get; } /// /// The number of bytes occupied by a single pixel in memory of the texture data. /// - public int BytesPerPixel { get; } + public byte BytesPerPixel { get; } /// /// The maximum number of components this format has defined (in RGBA order). /// - public int Components { get; } + public byte Components { get; } /// /// Whenever or not the texture format is a compressed format. Determined from block size. @@ -57,10 +62,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// The number of bytes occupied by a single pixel in memory of the texture data 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 /// /// For images, indicates the format specified on the shader. /// - public Format Format { get; } + public FormatInfo FormatInfo { get; } /// /// Shader texture host set index. @@ -58,17 +58,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs the texture binding information structure. /// /// The shader sampler target type - /// Format of the image as declared on the shader + /// Format of the image as declared on the shader /// Shader texture host set index /// The shader texture binding point /// For array of textures, this indicates the length of the array. A value of one indicates it is not an array /// Constant buffer slot where the texture handle is located /// The shader texture handle (read index into the texture constant buffer) /// The texture's usage flags, indicating how it is used in the shader - 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); } /// 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 @@ -75,6 +75,76 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly ConcurrentQueue _dereferenceQueue = new(); private TextureDescriptor _defaultDescriptor; + /// + /// List of textures that shares the same memory region, but have different formats. + /// + private class TextureAliasList + { + /// + /// Alias texture. + /// + /// Texture format + /// Texture + private readonly record struct Alias(Format Format, Texture Texture); + + /// + /// List of texture aliases. + /// + private readonly List _aliases; + + /// + /// Creates a new instance of the texture alias list. + /// + public TextureAliasList() + { + _aliases = new List(); + } + + /// + /// Adds a new texture alias. + /// + /// Alias format + /// Alias texture + public void Add(Format format, Texture texture) + { + _aliases.Add(new Alias(format, texture)); + texture.IncrementReferenceCount(); + } + + /// + /// Finds a texture with the requested format, or returns null if not found. + /// + /// Format to find + /// Texture with the requested format, or null if not found + public Texture Find(Format format) + { + foreach (var alias in _aliases) + { + if (alias.Format == format) + { + return alias.Texture; + } + } + + return null; + } + + /// + /// Removes all alias textures. + /// + public void Destroy() + { + foreach (var entry in _aliases) + { + entry.Texture.DecrementReferenceCount(); + } + + _aliases.Clear(); + } + } + + private readonly Dictionary _aliasLists; + /// /// Linked list node used on the texture pool cache. /// @@ -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(); } /// @@ -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. @@ -197,6 +267,51 @@ namespace Ryujinx.Graphics.Gpu.Image return ref GetInternal(id, out texture); } + /// + /// Gets the texture descriptor and texture with the given ID. + /// + /// + /// This method assumes that the pool has been manually synchronized before doing binding. + /// + /// ID of the texture. This is effectively a zero-based index + /// Texture format information + /// The texture with the given ID + /// The texture descriptor with the given ID + 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; + } + /// /// Checks if the pool was modified, and returns the last sequence number where a modification was detected. /// @@ -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); } } } @@ -622,6 +741,57 @@ namespace Ryujinx.Graphics.Gpu.Image component == SwizzleComponent.Green; } + /// + /// Changes the format on the texture information structure, and also adjusts the width for the new format if needed. + /// + /// Texture information + /// New format + /// Texture information with the new format + 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); + } + + /// + /// Removes all aliases for a texture. + /// + /// Texture to have the aliases removed + private void RemoveAliasList(Texture texture) + { + if (_aliasLists.TryGetValue(texture, out TextureAliasList aliasList)) + { + _aliasLists.Remove(texture); + aliasList.Destroy(); + } + } + /// /// Decrements the reference count of the texture. /// This indicates that the texture pool is not using it anymore. @@ -629,7 +799,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture to be deleted protected override void Delete(Texture item) { - item?.DecrementReferenceCount(this); + if (item != null) + { + item.DecrementReferenceCount(this); + RemoveAliasList(item); + } } public override void Dispose() diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 26d9501c..409867e0 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -509,7 +509,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (binding.IsImage) { - _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format); + _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture); } else { @@ -873,12 +873,11 @@ namespace Ryujinx.Graphics.Gpu.Memory ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - Format format, bool isImage) { _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); - _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage)); + _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage)); } /// @@ -897,12 +896,11 @@ namespace Ryujinx.Graphics.Gpu.Memory ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - int index, - Format format) + int index) { _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); - _bufferTextureArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index, format)); + _bufferTextureArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index)); } /// @@ -921,12 +919,11 @@ namespace Ryujinx.Graphics.Gpu.Memory ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - int index, - Format format) + int index) { _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); - _bufferImageArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index, format)); + _bufferImageArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index)); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs index fa79e4f9..a5338fa5 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs @@ -34,33 +34,26 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public int Index { get; } - /// - /// The image format for the binding. - /// - public Format Format { get; } - /// /// Create a new buffer texture binding. /// + /// Array /// Buffer texture /// Physical ranges of memory where the buffer texture data is located /// Binding info /// Index of the binding on the array - /// Binding format public BufferTextureArrayBinding( T array, ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - int index, - Format format) + int index) { Array = array; Texture = texture; Range = range; BindingInfo = bindingInfo; Index = index; - Format = format; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs index bf0beffa..1a3fde5b 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs @@ -30,11 +30,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public TextureBindingInfo BindingInfo { get; } - /// - /// The image format for the binding. - /// - public Format Format { get; } - /// /// Whether the binding is for an image or a sampler. /// @@ -47,21 +42,18 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Buffer texture /// Physical ranges of memory where the buffer texture data is located /// Binding info - /// Binding format /// Whether the binding is for an image or a sampler public BufferTextureBinding( ShaderStage stage, ITexture texture, MultiRange range, TextureBindingInfo bindingInfo, - Format format, bool isImage) { Stage = stage; Texture = texture; Range = range; BindingInfo = bindingInfo; - Format = format; IsImage = isImage; } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs index 51be00b6..018c5fdc 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs @@ -86,11 +86,11 @@ namespace Ryujinx.Graphics.Gpu.Shader ImageBindings[i] = stage.Info.Images.Select(descriptor => { Target target = ShaderTexture.GetTarget(descriptor.Type); - Format format = ShaderTexture.GetFormat(descriptor.Format); + FormatInfo formatInfo = ShaderTexture.GetFormatInfo(descriptor.Format); var result = new TextureBindingInfo( target, - format, + formatInfo, descriptor.Set, descriptor.Binding, descriptor.ArrayLength, diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 3b236853..59cd4c8a 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu bool isLinear, int gobBlocksInY, Format format, - int bytesPerPixel, + byte bytesPerPixel, ImageCrop crop, Action acquireCallback, Action releaseCallback, -- cgit v1.2.3