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

@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Vulkan
class DescriptorSetUpdater
{
private const ulong StorageBufferMaxMirrorable = 0x2000;
private const int ArrayGrowthSize = 16;
private record struct BufferRef
{
public Auto<DisposableBuffer> Buffer;
@ -65,6 +68,18 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private record struct ArrayRef<T>
{
public ShaderStage Stage;
public T Array;
public ArrayRef(ShaderStage stage, T array)
{
Stage = stage;
Array = array;
}
}
private readonly VulkanRenderer _gd;
private readonly Device _device;
private readonly PipelineBase _pipeline;
@ -78,6 +93,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly TextureBuffer[] _bufferImageRefs;
private readonly Format[] _bufferImageFormats;
private ArrayRef<TextureArray>[] _textureArrayRefs;
private ArrayRef<ImageArray>[] _imageArrayRefs;
private readonly DescriptorBufferInfo[] _uniformBuffers;
private readonly DescriptorBufferInfo[] _storageBuffers;
private readonly DescriptorImageInfo[] _textures;
@ -130,6 +148,9 @@ namespace Ryujinx.Graphics.Vulkan
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
_uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings];
_storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings];
_textures = new DescriptorImageInfo[Constants.MaxTexturesPerStage];
@ -263,10 +284,18 @@ namespace Ryujinx.Graphics.Vulkan
{
if (segment.Type == ResourceType.TextureAndSampler)
{
for (int i = 0; i < segment.Count; i++)
if (!segment.IsArray)
{
ref var texture = ref _textureRefs[segment.Binding + i];
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
for (int i = 0; i < segment.Count; i++)
{
ref var texture = ref _textureRefs[segment.Binding + i];
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
}
}
else
{
PipelineStageFlags stageFlags = _textureArrayRefs[segment.Binding].Stage.ConvertToPipelineStageFlags();
_textureArrayRefs[segment.Binding].Array?.QueueWriteToReadBarriers(cbs, stageFlags);
}
}
}
@ -275,10 +304,18 @@ namespace Ryujinx.Graphics.Vulkan
{
if (segment.Type == ResourceType.Image)
{
for (int i = 0; i < segment.Count; i++)
if (!segment.IsArray)
{
ref var image = ref _imageRefs[segment.Binding + i];
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
for (int i = 0; i < segment.Count; i++)
{
ref var image = ref _imageRefs[segment.Binding + i];
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
}
}
else
{
PipelineStageFlags stageFlags = _imageArrayRefs[segment.Binding].Stage.ConvertToPipelineStageFlags();
_imageArrayRefs[segment.Binding].Array?.QueueWriteToReadBarriers(cbs, stageFlags);
}
}
}
@ -455,6 +492,58 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void SetTextureArray(CommandBufferScoped cbs, ShaderStage stage, int binding, ITextureArray array)
{
if (_textureArrayRefs.Length <= binding)
{
Array.Resize(ref _textureArrayRefs, binding + ArrayGrowthSize);
}
if (_textureArrayRefs[binding].Stage != stage || _textureArrayRefs[binding].Array != array)
{
if (_textureArrayRefs[binding].Array != null)
{
_textureArrayRefs[binding].Array.Bound = false;
}
if (array is TextureArray textureArray)
{
textureArray.Bound = true;
textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
_textureArrayRefs[binding] = new ArrayRef<TextureArray>(stage, array as TextureArray);
SignalDirty(DirtyFlags.Texture);
}
}
public void SetImageArray(CommandBufferScoped cbs, ShaderStage stage, int binding, IImageArray array)
{
if (_imageArrayRefs.Length <= binding)
{
Array.Resize(ref _imageArrayRefs, binding + ArrayGrowthSize);
}
if (_imageArrayRefs[binding].Stage != stage || _imageArrayRefs[binding].Array != array)
{
if (_imageArrayRefs[binding].Array != null)
{
_imageArrayRefs[binding].Array.Bound = false;
}
if (array is ImageArray imageArray)
{
imageArray.Bound = true;
imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
_imageArrayRefs[binding] = new ArrayRef<ImageArray>(stage, array as ImageArray);
SignalDirty(DirtyFlags.Image);
}
}
public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
{
for (int i = 0; i < buffers.Length; i++)
@ -653,66 +742,94 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (setIndex == PipelineBase.TextureSetIndex)
{
if (segment.Type != ResourceType.BufferTexture)
if (!segment.IsArray)
{
Span<DescriptorImageInfo> textures = _textures;
for (int i = 0; i < count; i++)
if (segment.Type != ResourceType.BufferTexture)
{
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
Span<DescriptorImageInfo> textures = _textures;
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
for (int i = 0; i < count; i++)
{
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
{
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
}
if (texture.Sampler.Handle == 0)
{
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
}
}
if (texture.Sampler.Handle == 0)
{
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
}
tu.Push<DescriptorImageInfo>(textures[..count]);
}
else
{
Span<BufferView> bufferTextures = _bufferTextures;
tu.Push<DescriptorImageInfo>(textures[..count]);
for (int i = 0; i < count; i++)
{
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
}
tu.Push<BufferView>(bufferTextures[..count]);
}
}
else
{
Span<BufferView> bufferTextures = _bufferTextures;
for (int i = 0; i < count; i++)
if (segment.Type != ResourceType.BufferTexture)
{
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
tu.Push(_textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler));
}
else
{
tu.Push(_textureArrayRefs[binding].Array.GetBufferViews(cbs));
}
tu.Push<BufferView>(bufferTextures[..count]);
}
}
else if (setIndex == PipelineBase.ImageSetIndex)
{
if (segment.Type != ResourceType.BufferImage)
if (!segment.IsArray)
{
Span<DescriptorImageInfo> images = _images;
for (int i = 0; i < count; i++)
if (segment.Type != ResourceType.BufferImage)
{
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
}
Span<DescriptorImageInfo> images = _images;
tu.Push<DescriptorImageInfo>(images[..count]);
for (int i = 0; i < count; i++)
{
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
}
tu.Push<DescriptorImageInfo>(images[..count]);
}
else
{
Span<BufferView> bufferImages = _bufferImages;
for (int i = 0; i < count; i++)
{
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default;
}
tu.Push<BufferView>(bufferImages[..count]);
}
}
else
{
Span<BufferView> bufferImages = _bufferImages;
for (int i = 0; i < count; i++)
if (segment.Type != ResourceType.BufferTexture)
{
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default;
tu.Push(_imageArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture));
}
else
{
tu.Push(_imageArrayRefs[binding].Array.GetBufferViews(cbs));
}
tu.Push<BufferView>(bufferImages[..count]);
}
}
}
@ -825,6 +942,16 @@ namespace Ryujinx.Graphics.Vulkan
AdvancePdSequence();
}
public void ForceTextureDirty()
{
SignalDirty(DirtyFlags.Texture);
}
public void ForceImageDirty()
{
SignalDirty(DirtyFlags.Image);
}
private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
{
for (int i = 0; i < list.Length; i++)