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
|
@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
class DescriptorSetManager : IDisposable
|
||||
{
|
||||
public const uint MaxSets = 16;
|
||||
public const uint MaxSets = 8;
|
||||
|
||||
public class DescriptorPoolHolder : IDisposable
|
||||
{
|
||||
|
|
|
@ -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++)
|
||||
|
|
179
src/Ryujinx.Graphics.Vulkan/ImageArray.cs
Normal file
179
src/Ryujinx.Graphics.Vulkan/ImageArray.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class ImageArray : IImageArray
|
||||
{
|
||||
private readonly VulkanRenderer _gd;
|
||||
|
||||
private record struct TextureRef
|
||||
{
|
||||
public TextureStorage Storage;
|
||||
public TextureView View;
|
||||
public GAL.Format ImageFormat;
|
||||
}
|
||||
|
||||
private readonly TextureRef[] _textureRefs;
|
||||
private readonly TextureBuffer[] _bufferTextureRefs;
|
||||
|
||||
private readonly DescriptorImageInfo[] _textures;
|
||||
private readonly BufferView[] _bufferTextures;
|
||||
|
||||
private HashSet<TextureStorage> _storages;
|
||||
|
||||
private int _cachedCommandBufferIndex;
|
||||
private int _cachedSubmissionCount;
|
||||
|
||||
private readonly bool _isBuffer;
|
||||
|
||||
public bool Bound;
|
||||
|
||||
public ImageArray(VulkanRenderer gd, int size, bool isBuffer)
|
||||
{
|
||||
_gd = gd;
|
||||
|
||||
if (isBuffer)
|
||||
{
|
||||
_bufferTextureRefs = new TextureBuffer[size];
|
||||
_bufferTextures = new BufferView[size];
|
||||
}
|
||||
else
|
||||
{
|
||||
_textureRefs = new TextureRef[size];
|
||||
_textures = new DescriptorImageInfo[size];
|
||||
}
|
||||
|
||||
_storages = null;
|
||||
|
||||
_cachedCommandBufferIndex = -1;
|
||||
_cachedSubmissionCount = 0;
|
||||
|
||||
_isBuffer = isBuffer;
|
||||
}
|
||||
|
||||
public void SetFormats(int index, GAL.Format[] imageFormats)
|
||||
{
|
||||
for (int i = 0; i < imageFormats.Length; i++)
|
||||
{
|
||||
_textureRefs[index + i].ImageFormat = imageFormats[i];
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public void SetImages(int index, ITexture[] images)
|
||||
{
|
||||
for (int i = 0; i < images.Length; i++)
|
||||
{
|
||||
ITexture image = images[i];
|
||||
|
||||
if (image is TextureBuffer textureBuffer)
|
||||
{
|
||||
_bufferTextureRefs[index + i] = textureBuffer;
|
||||
}
|
||||
else if (image is TextureView view)
|
||||
{
|
||||
_textureRefs[index + i].Storage = view.Storage;
|
||||
_textureRefs[index + i].View = view;
|
||||
}
|
||||
else if (!_isBuffer)
|
||||
{
|
||||
_textureRefs[index + i].Storage = null;
|
||||
_textureRefs[index + i].View = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferTextureRefs[index + i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void SetDirty()
|
||||
{
|
||||
_cachedCommandBufferIndex = -1;
|
||||
_storages = null;
|
||||
|
||||
_gd.PipelineInternal.ForceImageDirty();
|
||||
}
|
||||
|
||||
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
|
||||
{
|
||||
HashSet<TextureStorage> storages = _storages;
|
||||
|
||||
if (storages == null)
|
||||
{
|
||||
storages = new HashSet<TextureStorage>();
|
||||
|
||||
for (int index = 0; index < _textureRefs.Length; index++)
|
||||
{
|
||||
if (_textureRefs[index].Storage != null)
|
||||
{
|
||||
storages.Add(_textureRefs[index].Storage);
|
||||
}
|
||||
}
|
||||
|
||||
_storages = storages;
|
||||
}
|
||||
|
||||
foreach (TextureStorage storage in storages)
|
||||
{
|
||||
storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stageFlags);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<DescriptorImageInfo> GetImageInfos(VulkanRenderer gd, CommandBufferScoped cbs, TextureView dummyTexture)
|
||||
{
|
||||
int submissionCount = gd.CommandBufferPool.GetSubmissionCount(cbs.CommandBufferIndex);
|
||||
|
||||
Span<DescriptorImageInfo> textures = _textures;
|
||||
|
||||
if (cbs.CommandBufferIndex == _cachedCommandBufferIndex && submissionCount == _cachedSubmissionCount)
|
||||
{
|
||||
return textures;
|
||||
}
|
||||
|
||||
_cachedCommandBufferIndex = cbs.CommandBufferIndex;
|
||||
_cachedSubmissionCount = submissionCount;
|
||||
|
||||
for (int i = 0; i < textures.Length; i++)
|
||||
{
|
||||
ref var texture = ref textures[i];
|
||||
ref var refs = ref _textureRefs[i];
|
||||
|
||||
if (i > 0 && _textureRefs[i - 1].View == refs.View && _textureRefs[i - 1].ImageFormat == refs.ImageFormat)
|
||||
{
|
||||
texture = textures[i - 1];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
texture.ImageLayout = ImageLayout.General;
|
||||
texture.ImageView = refs.View?.GetView(refs.ImageFormat).GetIdentityImageView().Get(cbs).Value ?? default;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
{
|
||||
texture.ImageView = dummyTexture.GetImageView().Get(cbs).Value;
|
||||
}
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<BufferView> GetBufferViews(CommandBufferScoped cbs)
|
||||
{
|
||||
Span<BufferView> bufferTextures = _bufferTextures;
|
||||
|
||||
for (int i = 0; i < bufferTextures.Length; i++)
|
||||
{
|
||||
bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, _textureRefs[i].ImageFormat, true) ?? default;
|
||||
}
|
||||
|
||||
return bufferTextures;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -898,6 +898,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_descriptorSetUpdater.SetImage(binding, image);
|
||||
}
|
||||
|
||||
public void SetImageArray(ShaderStage stage, int binding, IImageArray array)
|
||||
{
|
||||
_descriptorSetUpdater.SetImageArray(Cbs, stage, binding, array);
|
||||
}
|
||||
|
||||
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
||||
{
|
||||
if (buffer.Handle != BufferHandle.Null)
|
||||
|
@ -1146,6 +1151,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_descriptorSetUpdater.SetTextureAndSamplerIdentitySwizzle(Cbs, stage, binding, texture, sampler);
|
||||
}
|
||||
|
||||
public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array)
|
||||
{
|
||||
_descriptorSetUpdater.SetTextureArray(Cbs, stage, binding, array);
|
||||
}
|
||||
|
||||
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
|
||||
{
|
||||
PauseTransformFeedbackInternal();
|
||||
|
@ -1375,6 +1385,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
SignalCommandBufferChange();
|
||||
}
|
||||
|
||||
public void ForceTextureDirty()
|
||||
{
|
||||
_descriptorSetUpdater.ForceTextureDirty();
|
||||
}
|
||||
|
||||
public void ForceImageDirty()
|
||||
{
|
||||
_descriptorSetUpdater.ForceImageDirty();
|
||||
}
|
||||
|
||||
public unsafe void TextureBarrier()
|
||||
{
|
||||
MemoryBarrier memoryBarrier = new()
|
||||
|
|
|
@ -8,14 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
class PipelineLayoutCacheEntry
|
||||
{
|
||||
// Those were adjusted based on current descriptor usage and the descriptor counts usually used on pipeline layouts.
|
||||
// It might be a good idea to tweak them again if those change, or maybe find a way to calculate an optimal value dynamically.
|
||||
private const uint DefaultUniformBufferPoolCapacity = 19 * DescriptorSetManager.MaxSets;
|
||||
private const uint DefaultStorageBufferPoolCapacity = 16 * DescriptorSetManager.MaxSets;
|
||||
private const uint DefaultTexturePoolCapacity = 128 * DescriptorSetManager.MaxSets;
|
||||
private const uint DefaultImagePoolCapacity = 8 * DescriptorSetManager.MaxSets;
|
||||
|
||||
private const int MaxPoolSizesPerSet = 2;
|
||||
private const int MaxPoolSizesPerSet = 8;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
|
@ -24,6 +17,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
public PipelineLayout PipelineLayout { get; }
|
||||
|
||||
private readonly int[] _consumedDescriptorsPerSet;
|
||||
private readonly DescriptorPoolSize[][] _poolSizes;
|
||||
|
||||
private readonly DescriptorSetManager _descriptorSetManager;
|
||||
|
||||
private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache;
|
||||
private List<Auto<DescriptorSetCollection>>[] _currentDsCache;
|
||||
|
@ -65,6 +61,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||
|
||||
_consumedDescriptorsPerSet = new int[setDescriptors.Count];
|
||||
_poolSizes = new DescriptorPoolSize[setDescriptors.Count][];
|
||||
|
||||
Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet];
|
||||
|
||||
for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
|
||||
{
|
||||
|
@ -76,6 +75,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
|
||||
_consumedDescriptorsPerSet[setIndex] = count;
|
||||
_poolSizes[setIndex] = GetDescriptorPoolSizes(poolSizes, setDescriptors[setIndex], DescriptorSetManager.MaxSets).ToArray();
|
||||
}
|
||||
|
||||
if (usePushDescriptors)
|
||||
|
@ -83,6 +83,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_pdDescriptors = setDescriptors[0];
|
||||
_pdTemplates = new();
|
||||
}
|
||||
|
||||
_descriptorSetManager = new DescriptorSetManager(_device, setDescriptors.Count);
|
||||
}
|
||||
|
||||
public void UpdateCommandBufferIndex(int commandBufferIndex)
|
||||
|
@ -105,17 +107,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
int index = _dsCacheCursor[setIndex]++;
|
||||
if (index == list.Count)
|
||||
{
|
||||
Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet];
|
||||
poolSizes = GetDescriptorPoolSizes(poolSizes, setIndex);
|
||||
|
||||
int consumedDescriptors = _consumedDescriptorsPerSet[setIndex];
|
||||
|
||||
var dsc = _gd.DescriptorSetManager.AllocateDescriptorSet(
|
||||
var dsc = _descriptorSetManager.AllocateDescriptorSet(
|
||||
_gd.Api,
|
||||
DescriptorSetLayouts[setIndex],
|
||||
poolSizes,
|
||||
_poolSizes[setIndex],
|
||||
setIndex,
|
||||
consumedDescriptors,
|
||||
_consumedDescriptorsPerSet[setIndex],
|
||||
false);
|
||||
|
||||
list.Add(dsc);
|
||||
|
@ -127,28 +124,35 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return list[index];
|
||||
}
|
||||
|
||||
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, int setIndex)
|
||||
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier)
|
||||
{
|
||||
int count = 1;
|
||||
int count = 0;
|
||||
|
||||
switch (setIndex)
|
||||
for (int index = 0; index < setDescriptor.Descriptors.Count; index++)
|
||||
{
|
||||
case PipelineBase.UniformSetIndex:
|
||||
output[0] = new(DescriptorType.UniformBuffer, DefaultUniformBufferPoolCapacity);
|
||||
break;
|
||||
case PipelineBase.StorageSetIndex:
|
||||
output[0] = new(DescriptorType.StorageBuffer, DefaultStorageBufferPoolCapacity);
|
||||
break;
|
||||
case PipelineBase.TextureSetIndex:
|
||||
output[0] = new(DescriptorType.CombinedImageSampler, DefaultTexturePoolCapacity);
|
||||
output[1] = new(DescriptorType.UniformTexelBuffer, DefaultTexturePoolCapacity);
|
||||
count = 2;
|
||||
break;
|
||||
case PipelineBase.ImageSetIndex:
|
||||
output[0] = new(DescriptorType.StorageImage, DefaultImagePoolCapacity);
|
||||
output[1] = new(DescriptorType.StorageTexelBuffer, DefaultImagePoolCapacity);
|
||||
count = 2;
|
||||
break;
|
||||
ResourceDescriptor descriptor = setDescriptor.Descriptors[index];
|
||||
DescriptorType descriptorType = descriptor.Type.Convert();
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int poolSizeIndex = 0; poolSizeIndex < count; poolSizeIndex++)
|
||||
{
|
||||
if (output[poolSizeIndex].Type == descriptorType)
|
||||
{
|
||||
output[poolSizeIndex].DescriptorCount += (uint)descriptor.Count * multiplier;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
output[count++] = new DescriptorPoolSize()
|
||||
{
|
||||
Type = descriptorType,
|
||||
DescriptorCount = (uint)descriptor.Count,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return output[..count];
|
||||
|
@ -206,6 +210,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
_gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null);
|
||||
}
|
||||
|
||||
_descriptorSetManager.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
public readonly int Count;
|
||||
public readonly ResourceType Type;
|
||||
public readonly ResourceStages Stages;
|
||||
public readonly bool IsArray;
|
||||
|
||||
public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages)
|
||||
public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray)
|
||||
{
|
||||
Binding = binding;
|
||||
Count = count;
|
||||
Type = type;
|
||||
Stages = stages;
|
||||
IsArray = isArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
};
|
||||
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding, type, stages));
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -241,7 +241,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
if (currentDescriptor.Binding + currentCount != descriptor.Binding ||
|
||||
currentDescriptor.Type != descriptor.Type ||
|
||||
currentDescriptor.Stages != descriptor.Stages)
|
||||
currentDescriptor.Stages != descriptor.Stages ||
|
||||
currentDescriptor.Count > 1 ||
|
||||
descriptor.Count > 1)
|
||||
{
|
||||
if (currentCount != 0)
|
||||
{
|
||||
|
@ -249,7 +251,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
currentDescriptor.Binding,
|
||||
currentCount,
|
||||
currentDescriptor.Type,
|
||||
currentDescriptor.Stages));
|
||||
currentDescriptor.Stages,
|
||||
currentDescriptor.Count > 1));
|
||||
}
|
||||
|
||||
currentDescriptor = descriptor;
|
||||
|
@ -267,7 +270,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
currentDescriptor.Binding,
|
||||
currentCount,
|
||||
currentDescriptor.Type,
|
||||
currentDescriptor.Stages));
|
||||
currentDescriptor.Stages,
|
||||
currentDescriptor.Count > 1));
|
||||
}
|
||||
|
||||
segments[setIndex] = currentSegments.ToArray();
|
||||
|
@ -293,7 +297,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||
currentUsage.Type != usage.Type ||
|
||||
currentUsage.Stages != usage.Stages)
|
||||
currentUsage.Stages != usage.Stages ||
|
||||
currentUsage.ArrayLength > 1 ||
|
||||
usage.ArrayLength > 1)
|
||||
{
|
||||
if (currentCount != 0)
|
||||
{
|
||||
|
@ -301,11 +307,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages));
|
||||
currentUsage.Stages,
|
||||
currentUsage.ArrayLength > 1));
|
||||
}
|
||||
|
||||
currentUsage = usage;
|
||||
currentCount = 1;
|
||||
currentCount = usage.ArrayLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -319,7 +326,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages));
|
||||
currentUsage.Stages,
|
||||
currentUsage.ArrayLength > 1));
|
||||
}
|
||||
|
||||
segments[setIndex] = currentSegments.ToArray();
|
||||
|
@ -344,7 +352,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
if (segments != null && segments.Length > 0)
|
||||
{
|
||||
templates[setIndex] = new DescriptorSetTemplate(_gd, _device, segments, _plce, IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics, setIndex);
|
||||
templates[setIndex] = new DescriptorSetTemplate(
|
||||
_gd,
|
||||
_device,
|
||||
segments,
|
||||
_plce,
|
||||
IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics,
|
||||
setIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
194
src/Ryujinx.Graphics.Vulkan/TextureArray.cs
Normal file
194
src/Ryujinx.Graphics.Vulkan/TextureArray.cs
Normal file
|
@ -0,0 +1,194 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class TextureArray : ITextureArray
|
||||
{
|
||||
private readonly VulkanRenderer _gd;
|
||||
|
||||
private struct TextureRef
|
||||
{
|
||||
public TextureStorage Storage;
|
||||
public Auto<DisposableImageView> View;
|
||||
public Auto<DisposableSampler> Sampler;
|
||||
}
|
||||
|
||||
private readonly TextureRef[] _textureRefs;
|
||||
private readonly TextureBuffer[] _bufferTextureRefs;
|
||||
|
||||
private readonly DescriptorImageInfo[] _textures;
|
||||
private readonly BufferView[] _bufferTextures;
|
||||
|
||||
private HashSet<TextureStorage> _storages;
|
||||
|
||||
private int _cachedCommandBufferIndex;
|
||||
private int _cachedSubmissionCount;
|
||||
|
||||
private readonly bool _isBuffer;
|
||||
|
||||
public bool Bound;
|
||||
|
||||
public TextureArray(VulkanRenderer gd, int size, bool isBuffer)
|
||||
{
|
||||
_gd = gd;
|
||||
|
||||
if (isBuffer)
|
||||
{
|
||||
_bufferTextureRefs = new TextureBuffer[size];
|
||||
_bufferTextures = new BufferView[size];
|
||||
}
|
||||
else
|
||||
{
|
||||
_textureRefs = new TextureRef[size];
|
||||
_textures = new DescriptorImageInfo[size];
|
||||
}
|
||||
|
||||
_storages = null;
|
||||
|
||||
_cachedCommandBufferIndex = -1;
|
||||
_cachedSubmissionCount = 0;
|
||||
|
||||
_isBuffer = isBuffer;
|
||||
}
|
||||
|
||||
public void SetSamplers(int index, ISampler[] samplers)
|
||||
{
|
||||
for (int i = 0; i < samplers.Length; i++)
|
||||
{
|
||||
ISampler sampler = samplers[i];
|
||||
|
||||
if (sampler is SamplerHolder samplerHolder)
|
||||
{
|
||||
_textureRefs[index + i].Sampler = samplerHolder.GetSampler();
|
||||
}
|
||||
else
|
||||
{
|
||||
_textureRefs[index + i].Sampler = default;
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public void SetTextures(int index, ITexture[] textures)
|
||||
{
|
||||
for (int i = 0; i < textures.Length; i++)
|
||||
{
|
||||
ITexture texture = textures[i];
|
||||
|
||||
if (texture is TextureBuffer textureBuffer)
|
||||
{
|
||||
_bufferTextureRefs[index + i] = textureBuffer;
|
||||
}
|
||||
else if (texture is TextureView view)
|
||||
{
|
||||
_textureRefs[index + i].Storage = view.Storage;
|
||||
_textureRefs[index + i].View = view.GetImageView();
|
||||
}
|
||||
else if (!_isBuffer)
|
||||
{
|
||||
_textureRefs[index + i].Storage = null;
|
||||
_textureRefs[index + i].View = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferTextureRefs[index + i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void SetDirty()
|
||||
{
|
||||
_cachedCommandBufferIndex = -1;
|
||||
_storages = null;
|
||||
|
||||
_gd.PipelineInternal.ForceTextureDirty();
|
||||
}
|
||||
|
||||
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
|
||||
{
|
||||
HashSet<TextureStorage> storages = _storages;
|
||||
|
||||
if (storages == null)
|
||||
{
|
||||
storages = new HashSet<TextureStorage>();
|
||||
|
||||
for (int index = 0; index < _textureRefs.Length; index++)
|
||||
{
|
||||
if (_textureRefs[index].Storage != null)
|
||||
{
|
||||
storages.Add(_textureRefs[index].Storage);
|
||||
}
|
||||
}
|
||||
|
||||
_storages = storages;
|
||||
}
|
||||
|
||||
foreach (TextureStorage storage in storages)
|
||||
{
|
||||
storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stageFlags);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<DescriptorImageInfo> GetImageInfos(VulkanRenderer gd, CommandBufferScoped cbs, TextureView dummyTexture, SamplerHolder dummySampler)
|
||||
{
|
||||
int submissionCount = gd.CommandBufferPool.GetSubmissionCount(cbs.CommandBufferIndex);
|
||||
|
||||
Span<DescriptorImageInfo> textures = _textures;
|
||||
|
||||
if (cbs.CommandBufferIndex == _cachedCommandBufferIndex && submissionCount == _cachedSubmissionCount)
|
||||
{
|
||||
return textures;
|
||||
}
|
||||
|
||||
_cachedCommandBufferIndex = cbs.CommandBufferIndex;
|
||||
_cachedSubmissionCount = submissionCount;
|
||||
|
||||
for (int i = 0; i < textures.Length; i++)
|
||||
{
|
||||
ref var texture = ref textures[i];
|
||||
ref var refs = ref _textureRefs[i];
|
||||
|
||||
if (i > 0 && _textureRefs[i - 1].View == refs.View && _textureRefs[i - 1].Sampler == refs.Sampler)
|
||||
{
|
||||
texture = textures[i - 1];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
texture.ImageLayout = ImageLayout.General;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<BufferView> GetBufferViews(CommandBufferScoped cbs)
|
||||
{
|
||||
Span<BufferView> bufferTextures = _bufferTextures;
|
||||
|
||||
for (int i = 0; i < bufferTextures.Length; i++)
|
||||
{
|
||||
bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, false) ?? default;
|
||||
}
|
||||
|
||||
return bufferTextures;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,7 +48,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
internal MemoryAllocator MemoryAllocator { get; private set; }
|
||||
internal HostMemoryAllocator HostMemoryAllocator { get; private set; }
|
||||
internal CommandBufferPool CommandBufferPool { get; private set; }
|
||||
internal DescriptorSetManager DescriptorSetManager { get; private set; }
|
||||
internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
|
||||
internal BackgroundResources BackgroundResources { get; private set; }
|
||||
internal Action<Action> InterruptAction { get; private set; }
|
||||
|
@ -414,8 +413,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||
|
||||
DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts);
|
||||
|
||||
PipelineLayoutCache = new PipelineLayoutCache();
|
||||
|
||||
BackgroundResources = new BackgroundResources(this, _device);
|
||||
|
@ -507,6 +504,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return BufferManager.CreateSparse(this, storageBuffers);
|
||||
}
|
||||
|
||||
public IImageArray CreateImageArray(int size, bool isBuffer)
|
||||
{
|
||||
return new ImageArray(this, size, isBuffer);
|
||||
}
|
||||
|
||||
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
|
||||
{
|
||||
bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute;
|
||||
|
@ -539,6 +541,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return CreateTextureView(info);
|
||||
}
|
||||
|
||||
public ITextureArray CreateTextureArray(int size, bool isBuffer)
|
||||
{
|
||||
return new TextureArray(this, size, isBuffer);
|
||||
}
|
||||
|
||||
internal TextureView CreateTextureView(TextureCreateInfo info)
|
||||
{
|
||||
// This should be disposed when all views are destroyed.
|
||||
|
@ -925,7 +932,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
HelperShader.Dispose();
|
||||
_pipeline.Dispose();
|
||||
BufferManager.Dispose();
|
||||
DescriptorSetManager.Dispose();
|
||||
PipelineLayoutCache.Dispose();
|
||||
Barriers.Dispose();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue