Add support for large sampler arrays on Vulkan (#6489)

* Add support for large sampler arrays on Vulkan

* Shader cache version bump

* Format whitespace

* Move DescriptorSetManager to PipelineLayoutCacheEntry to allow different pool sizes per layout

* Handle array textures with different types on the same buffer

* Somewhat better caching system

* Avoid useless buffer data modification checks

* Move redundant bindings update checking to the backend

* Fix an issue where texture arrays would get the same bindings across stages on Vulkan

* Backport some fixes from part 2

* Fix typo

* PR feedback

* Format whitespace

* Add some missing XML docs
This commit is contained in:
gdkchan 2024-04-07 18:25:55 -03:00 committed by GitHub
parent 808803d97a
commit 3e6e0e4afa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
83 changed files with 3263 additions and 955 deletions

View file

@ -1,12 +1,13 @@
using Ryujinx.Graphics.Shader;
using Ryujinx.Memory.Range;
using System;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Memory range used for buffers.
/// </summary>
readonly struct BufferBounds
readonly struct BufferBounds : IEquatable<BufferBounds>
{
/// <summary>
/// Physical memory ranges where the buffer is mapped.
@ -33,5 +34,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
Range = range;
Flags = flags;
}
public override bool Equals(object obj)
{
return obj is BufferBounds bounds && Equals(bounds);
}
public bool Equals(BufferBounds bounds)
{
return Range == bounds.Range && Flags == bounds.Flags;
}
public bool Equals(ref BufferBounds bounds)
{
return Range == bounds.Range && Flags == bounds.Flags;
}
public override int GetHashCode()
{
return HashCode.Combine(Range, Flags);
}
}
}

View file

@ -27,6 +27,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly VertexBuffer[] _vertexBuffers;
private readonly BufferBounds[] _transformFeedbackBuffers;
private readonly List<BufferTextureBinding> _bufferTextures;
private readonly List<BufferTextureArrayBinding<ITextureArray>> _bufferTextureArrays;
private readonly List<BufferTextureArrayBinding<IImageArray>> _bufferImageArrays;
private readonly BufferAssignment[] _ranges;
/// <summary>
@ -140,11 +142,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
_bufferTextures = new List<BufferTextureBinding>();
_bufferTextureArrays = new List<BufferTextureArrayBinding<ITextureArray>>();
_bufferImageArrays = new List<BufferTextureArrayBinding<IImageArray>>();
_ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
}
/// <summary>
/// Sets the memory range with the index buffer data, to be used for subsequent draw calls.
/// </summary>
@ -418,6 +421,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
}
/// <summary>
/// Gets the size of the compute uniform buffer currently bound at the given index.
/// </summary>
/// <param name="index">Index of the uniform buffer binding</param>
/// <returns>The uniform buffer size, or an undefined value if the buffer is not currently bound</returns>
public int GetComputeUniformBufferSize(int index)
{
return (int)_cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Size;
}
/// <summary>
/// Gets the address of the graphics uniform buffer currently bound at the given index.
/// </summary>
@ -429,6 +442,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
}
/// <summary>
/// Gets the size of the graphics uniform buffer currently bound at the given index.
/// </summary>
/// <param name="stage">Index of the shader stage</param>
/// <param name="index">Index of the uniform buffer binding</param>
/// <returns>The uniform buffer size, or an undefined value if the buffer is not currently bound</returns>
public int GetGraphicsUniformBufferSize(int stage, int index)
{
return (int)_gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Size;
}
/// <summary>
/// Gets the bounds of the uniform buffer currently bound at the given index.
/// </summary>
@ -459,7 +483,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
CommitBufferTextureBindings();
CommitBufferTextureBindings(bufferCache);
// Force rebind after doing compute work.
Rebind();
@ -470,14 +494,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Commit any queued buffer texture bindings.
/// </summary>
private void CommitBufferTextureBindings()
/// <param name="bufferCache">Buffer cache</param>
private void CommitBufferTextureBindings(BufferCache bufferCache)
{
if (_bufferTextures.Count > 0)
{
foreach (var binding in _bufferTextures)
{
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Range, isStore);
var range = bufferCache.GetBufferRange(binding.Range, isStore);
binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated.
@ -494,6 +519,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferTextures.Clear();
}
if (_bufferTextureArrays.Count > 0 || _bufferImageArrays.Count > 0)
{
ITexture[] textureArray = new ITexture[1];
foreach (var binding in _bufferTextureArrays)
{
var range = bufferCache.GetBufferRange(binding.Range);
binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture;
binding.Array.SetTextures(binding.Index, textureArray);
}
foreach (var binding in _bufferImageArrays)
{
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
var range = bufferCache.GetBufferRange(binding.Range, isStore);
binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture;
binding.Array.SetImages(binding.Index, textureArray);
}
_bufferTextureArrays.Clear();
_bufferImageArrays.Clear();
}
}
/// <summary>
@ -676,7 +728,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
UpdateBuffers(_gpUniformBuffers);
}
CommitBufferTextureBindings();
CommitBufferTextureBindings(bufferCache);
_rebind = false;
@ -828,6 +880,50 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage));
}
/// <summary>
/// Sets the buffer storage of a buffer texture array element. This will be bound when the buffer manager commits bindings.
/// </summary>
/// <param name="array">Texture array where the element will be inserted</param>
/// <param name="texture">Buffer texture</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="index">Index of the binding on the array</param>
/// <param name="format">Format of the buffer texture</param>
public void SetBufferTextureStorage(
ITextureArray array,
ITexture texture,
MultiRange range,
TextureBindingInfo bindingInfo,
int index,
Format format)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range);
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index, format));
}
/// <summary>
/// Sets the buffer storage of a buffer image array element. This will be bound when the buffer manager commits bindings.
/// </summary>
/// <param name="array">Image array where the element will be inserted</param>
/// <param name="texture">Buffer texture</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="index">Index of the binding on the array</param>
/// <param name="format">Format of the buffer texture</param>
public void SetBufferTextureStorage(
IImageArray array,
ITexture texture,
MultiRange range,
TextureBindingInfo bindingInfo,
int index,
Format format)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range);
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index, format));
}
/// <summary>
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary>

View file

@ -0,0 +1,66 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Memory.Range;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// A buffer binding to apply to a buffer texture array element.
/// </summary>
readonly struct BufferTextureArrayBinding<T>
{
/// <summary>
/// Backend texture or image array.
/// </summary>
public T Array { get; }
/// <summary>
/// The buffer texture.
/// </summary>
public ITexture Texture { get; }
/// <summary>
/// Physical ranges of memory where the buffer texture data is located.
/// </summary>
public MultiRange Range { get; }
/// <summary>
/// The image or sampler binding info for the buffer texture.
/// </summary>
public TextureBindingInfo BindingInfo { get; }
/// <summary>
/// Index of the binding on the array.
/// </summary>
public int Index { get; }
/// <summary>
/// The image format for the binding.
/// </summary>
public Format Format { get; }
/// <summary>
/// Create a new buffer texture binding.
/// </summary>
/// <param name="texture">Buffer texture</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info</param>
/// <param name="index">Index of the binding on the array</param>
/// <param name="format">Binding format</param>
public BufferTextureArrayBinding(
T array,
ITexture texture,
MultiRange range,
TextureBindingInfo bindingInfo,
int index,
Format format)
{
Array = array;
Texture = texture;
Range = range;
BindingInfo = bindingInfo;
Index = index;
Format = format;
}
}
}