Fix NRE in Vulkan TextureArray/ImageArray for buffer-targeted arrays

When isBuffer=true the array allocates _bufferTextureRefs and leaves
_textureRefs null. SetSamplers and QueueWriteToReadBarriers dereferenced
_textureRefs unconditionally, and the TextureView branch of SetTextures
and SetImages did the same when a non-buffer view landed in a
buffer-targeted array. Reached in practice when shaders sample from
texel-buffer-typed array bindings with mixed contents.

Branch SetTextures and SetImages on _isBuffer first, then on input type.
A TextureView in a buffer-mode array, and a TextureBuffer in a
non-buffer-mode array, are both dropped to null. The latter was a
pre-existing mirror bug, unreached by current callers.

Early-return from SetSamplers and QueueWriteToReadBarriers for
buffer-mode arrays: texel-buffer descriptors carry no sampler state and
storage barriers do not apply.
This commit is contained in:
Jakob Linke 2026-05-22 15:57:04 +02:00
parent 81468c1d25
commit e2952ee6db
2 changed files with 49 additions and 34 deletions

View file

@ -53,27 +53,27 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImages(int index, ITexture[] images)
{
for (int i = 0; i < images.Length; i++)
if (_isBuffer)
{
ITexture image = images[i];
if (image is TextureBuffer textureBuffer)
for (int i = 0; i < images.Length; i++)
{
_bufferTextureRefs[index + i] = textureBuffer;
_bufferTextureRefs[index + i] = images[i] as TextureBuffer;
}
else if (image is TextureView view)
}
else
{
for (int i = 0; i < images.Length; i++)
{
_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;
if (images[i] is TextureView view)
{
_textureRefs[index + i].Storage = view.Storage;
_textureRefs[index + i].View = view;
}
else
{
_textureRefs[index + i].Storage = null;
_textureRefs[index + i].View = default;
}
}
}
@ -89,6 +89,11 @@ namespace Ryujinx.Graphics.Vulkan
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
{
if (_isBuffer)
{
return;
}
HashSet<TextureStorage> storages = _storages;
if (storages == null)

View file

@ -54,6 +54,11 @@ namespace Ryujinx.Graphics.Vulkan
public void SetSamplers(int index, ISampler[] samplers)
{
if (_isBuffer)
{
return;
}
for (int i = 0; i < samplers.Length; i++)
{
ISampler sampler = samplers[i];
@ -73,27 +78,27 @@ namespace Ryujinx.Graphics.Vulkan
public void SetTextures(int index, ITexture[] textures)
{
for (int i = 0; i < textures.Length; i++)
if (_isBuffer)
{
ITexture texture = textures[i];
if (texture is TextureBuffer textureBuffer)
for (int i = 0; i < textures.Length; i++)
{
_bufferTextureRefs[index + i] = textureBuffer;
_bufferTextureRefs[index + i] = textures[i] as TextureBuffer;
}
else if (texture is TextureView view)
}
else
{
for (int i = 0; i < textures.Length; i++)
{
_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;
if (textures[i] is TextureView view)
{
_textureRefs[index + i].Storage = view.Storage;
_textureRefs[index + i].View = view.GetImageView();
}
else
{
_textureRefs[index + i].Storage = null;
_textureRefs[index + i].View = default;
}
}
}
@ -109,6 +114,11 @@ namespace Ryujinx.Graphics.Vulkan
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
{
if (_isBuffer)
{
return;
}
HashSet<TextureStorage> storages = _storages;
if (storages == null)