Allow texture arrays to use separate descriptor sets on Vulkan (#6870)

* Report base and extra sets from the backend

* Pass texture set index everywhere

* Key textures using set and binding (rather than just binding)

* Start using extra sets for array textures

* Shader cache version bump

* Separate new commands, some PR feedback

* Introduce new manual descriptor set reservation method that prevents it from being used by something else while owned by an array

* Move bind extra sets logic to new method

* Should only use separate array is MaximumExtraSets is not zero

* Format whitespace
This commit is contained in:
gdkchan 2024-05-26 13:30:19 -03:00 committed by GitHub
parent 4cc00bb4b1
commit 53d096e392
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 996 additions and 262 deletions

View file

@ -69,17 +69,7 @@ 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 record struct ArrayRef<T>(ShaderStage Stage, T Array);
private readonly VulkanRenderer _gd;
private readonly Device _device;
@ -97,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
private ArrayRef<TextureArray>[] _textureArrayRefs;
private ArrayRef<ImageArray>[] _imageArrayRefs;
private ArrayRef<TextureArray>[] _textureArrayExtraRefs;
private ArrayRef<ImageArray>[] _imageArrayExtraRefs;
private readonly DescriptorBufferInfo[] _uniformBuffers;
private readonly DescriptorBufferInfo[] _storageBuffers;
private readonly DescriptorImageInfo[] _textures;
@ -152,6 +145,9 @@ namespace Ryujinx.Graphics.Vulkan
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
_textureArrayExtraRefs = Array.Empty<ArrayRef<TextureArray>>();
_imageArrayExtraRefs = Array.Empty<ArrayRef<ImageArray>>();
_uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings];
_storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings];
_textures = new DescriptorImageInfo[Constants.MaxTexturesPerStage];
@ -495,25 +491,39 @@ 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);
}
ref ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _textureArrayRefs, binding, ArrayGrowthSize);
if (_textureArrayRefs[binding].Stage != stage || _textureArrayRefs[binding].Array != array)
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
if (_textureArrayRefs[binding].Array != null)
{
_textureArrayRefs[binding].Array.Bound = false;
}
arrayRef.Array?.DecrementBindCount();
if (array is TextureArray textureArray)
{
textureArray.Bound = true;
textureArray.IncrementBindCount();
textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
_textureArrayRefs[binding] = new ArrayRef<TextureArray>(stage, array as TextureArray);
arrayRef = new ArrayRef<TextureArray>(stage, array as TextureArray);
SignalDirty(DirtyFlags.Texture);
}
}
public void SetTextureArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, ITextureArray array)
{
ref ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _textureArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef.Array?.DecrementBindCount();
if (array is TextureArray textureArray)
{
textureArray.IncrementBindCount();
textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
arrayRef = new ArrayRef<TextureArray>(stage, array as TextureArray);
SignalDirty(DirtyFlags.Texture);
}
@ -521,30 +531,56 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImageArray(CommandBufferScoped cbs, ShaderStage stage, int binding, IImageArray array)
{
if (_imageArrayRefs.Length <= binding)
{
Array.Resize(ref _imageArrayRefs, binding + ArrayGrowthSize);
}
ref ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _imageArrayRefs, binding, ArrayGrowthSize);
if (_imageArrayRefs[binding].Stage != stage || _imageArrayRefs[binding].Array != array)
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
if (_imageArrayRefs[binding].Array != null)
{
_imageArrayRefs[binding].Array.Bound = false;
}
arrayRef.Array?.DecrementBindCount();
if (array is ImageArray imageArray)
{
imageArray.Bound = true;
imageArray.IncrementBindCount();
imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
_imageArrayRefs[binding] = new ArrayRef<ImageArray>(stage, array as ImageArray);
arrayRef = new ArrayRef<ImageArray>(stage, array as ImageArray);
SignalDirty(DirtyFlags.Image);
}
}
public void SetImageArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, IImageArray array)
{
ref ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _imageArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef.Array?.DecrementBindCount();
if (array is ImageArray imageArray)
{
imageArray.IncrementBindCount();
imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
arrayRef = new ArrayRef<ImageArray>(stage, array as ImageArray);
SignalDirty(DirtyFlags.Image);
}
}
private static ref ArrayRef<T> GetArrayRef<T>(ref ArrayRef<T>[] array, int index, int growthSize = 1)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
if (array.Length <= index)
{
Array.Resize(ref array, index + growthSize);
}
return ref array[index];
}
public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
{
for (int i = 0; i < buffers.Length; i++)
@ -594,31 +630,40 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
var program = _program;
if (_dirty.HasFlag(DirtyFlags.Uniform))
{
if (_program.UsePushDescriptors)
if (program.UsePushDescriptors)
{
UpdateAndBindUniformBufferPd(cbs, pbp);
UpdateAndBindUniformBufferPd(cbs);
}
else
{
UpdateAndBind(cbs, PipelineBase.UniformSetIndex, pbp);
UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp);
}
}
if (_dirty.HasFlag(DirtyFlags.Storage))
{
UpdateAndBind(cbs, PipelineBase.StorageSetIndex, pbp);
UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp);
}
if (_dirty.HasFlag(DirtyFlags.Texture))
{
UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp);
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
}
if (_dirty.HasFlag(DirtyFlags.Image))
{
UpdateAndBind(cbs, PipelineBase.ImageSetIndex, pbp);
UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp);
}
if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts)
{
// Program is using extra sets, we need to bind those too.
BindExtraSets(cbs, program, pbp);
}
_dirty = DirtyFlags.None;
@ -658,9 +703,8 @@ namespace Ryujinx.Graphics.Vulkan
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
private void UpdateAndBind(CommandBufferScoped cbs, ShaderCollection program, int setIndex, PipelineBindPoint pbp)
{
var program = _program;
var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0)
@ -869,7 +913,7 @@ namespace Ryujinx.Graphics.Vulkan
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs)
{
int sequence = _pdSequence;
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
@ -933,6 +977,56 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void BindExtraSets(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
{
for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < program.BindingSegments.Length; setIndex++)
{
var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0)
{
continue;
}
ResourceBindingSegment segment = bindingSegments[0];
if (segment.IsArray)
{
DescriptorSet[] sets = null;
if (segment.Type == ResourceType.Texture ||
segment.Type == ResourceType.Sampler ||
segment.Type == ResourceType.TextureAndSampler ||
segment.Type == ResourceType.BufferTexture)
{
sets = _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets(
_device,
cbs,
_templateUpdater,
program,
setIndex,
_dummyTexture,
_dummySampler);
}
else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage)
{
sets = _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets(
_device,
cbs,
_templateUpdater,
program,
setIndex,
_dummyTexture);
}
if (sets != null)
{
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
}
}
}
}
public void SignalCommandBufferChange()
{
_updateDescriptorCacheCbIndex = true;