Ryujinx/src/Ryujinx.Graphics.Vulkan/TextureArray.cs
gdkchan 3e6e0e4afa
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
2024-04-07 18:25:55 -03:00

195 lines
5.8 KiB
C#

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;
}
}
}