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:
parent
808803d97a
commit
3e6e0e4afa
83 changed files with 3263 additions and 955 deletions
|
@ -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++)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue