From 3e6e0e4afaa3c3ffb118cb17b61feb16966a7eeb Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 7 Apr 2024 18:25:55 -0300 Subject: 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 --- .../DescriptorSetUpdater.cs | 205 +++++++++++++++++---- 1 file changed, 166 insertions(+), 39 deletions(-) (limited to 'src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs') diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 76568602..a0299a37 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -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 Buffer; @@ -65,6 +68,18 @@ namespace Ryujinx.Graphics.Vulkan } } + private record struct ArrayRef + { + 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[] _textureArrayRefs; + private ArrayRef[] _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>(); + _imageArrayRefs = Array.Empty>(); + _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(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(stage, array as ImageArray); + + SignalDirty(DirtyFlags.Image); + } + } + public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan 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 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 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) + tu.Push(textures[..count]); + } + else + { + Span bufferTextures = _bufferTextures; + + for (int i = 0; i < count; i++) { - texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; + bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; } - } - tu.Push(textures[..count]); + tu.Push(bufferTextures[..count]); + } } else { - Span 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(bufferTextures[..count]); } } else if (setIndex == PipelineBase.ImageSetIndex) { - if (segment.Type != ResourceType.BufferImage) + if (!segment.IsArray) { - Span 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 images = _images; + + for (int i = 0; i < count; i++) + { + images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default; + } + + tu.Push(images[..count]); } + else + { + Span bufferImages = _bufferImages; - tu.Push(images[..count]); + for (int i = 0; i < count; i++) + { + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default; + } + + tu.Push(bufferImages[..count]); + } } else { - Span 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(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 from, Auto to) { for (int i = 0; i < list.Length; i++) -- cgit v1.2.3