<p>I finally got an answer on the <a href="https://community.amd.com/t5/opengl-vulkan/vulkan-fragment-shader-bug-when-using-array-of-textures/m-p/600999/thread-id/4659#M4661" rel="nofollow noreferrer">AMD Forums</a>.</p>
<p>The issue was that you need to tell the GPU when you plan on indexing resource arrays with non uniform values.</p>
<p>My shader now looks like this:</p>
<pre><code>#version 450
#extension GL_EXT_nonuniform_qualifier : require

layout(set=0,binding=0)uniform sampler2D tex_samplers[];

layout(location=0)in vec2 uv_from_vertex_shader;
layout(location=1)in vec3 normal_from_vertex_shader;
layout(location=2)in flat uint tex_id_from_vertex_shader;


layout(location=0)out vec4 output_colour;

void main(){
    vec4 albedo = texture(tex_samplers[nonuniformEXT(tex_id_from_vertex_shader)], uv_from_vertex_shader);
    float light = clamp(dot(normal_from_vertex_shader, normalize(vec3(1,1,1))), 0.2, 1);
    output_colour =  vec4(albedo.rgb * light, albedo.a);
}
</code></pre>
<p>The important part is the <code>nonuniformEXT</code> that wraps the index</p>
<p>It is also worth mentioning that technically this also requires that you enable <code>shaderSampledImageArrayNonUniformIndexing</code> on your <code>VkPhysicalDeviceDescriptorIndexingFeatures</code></p>

CC BY-SA 4.0
2023-04-20T21:36:17.800Z
Do you have the validation layers turned on? This error should have been reported by the layers.
pmw1234
CC BY-SA 4.0
2023-04-21T22:39:06.450Z
Yeah I do have them enabled. Perhaps there is some specific config needed to catch this flavour of error but this didn't show up for me
InvalidOverhead
CC BY-SA 4.0
2023-04-22T11:19:07.907Z
In the Vulkan Configurator under "Validation" in  VK_LAYER_KHRONOS_validation->Validation Areas->Shader-Based->GPU-Assisted->Check descriptor indexing accesses Should trigger the error.
pmw1234
CC BY-SA 4.0
2023-04-19T12:54:22.313Z
2023-04-19T12:54:22.313Z
InvalidOverhead
<p>I originally asked this question on the gamdev stack exchange but was told by a friend I would be better served here.</p>
<p><a href="https://gamedev.stackexchange.com/questions/205276/why-do-texture-samplers-seem-to-bleed-into-other-instances-when-rendering-multip">Original Question</a></p>
<p>So I will start off by laying out the relevant structure of my program.</p>
<p>I am using vulkan to render the same mesh over a few instances. To do this I have:</p>
<ul>
<li>a vertex buffer, used per vertex, which is an array of the following structure</li>
</ul>
<pre class="lang-rust prettyprint-override"><code>pub struct ShaderVertexData {
    pub position: na::Vector3&lt;f32&gt;,
    pub uv: na::Vector2&lt;f32&gt;,
    pub normal: na::Vector3&lt;f32&gt;,
}
</code></pre>
<ul>
<li>an &quot;instance&quot; buffer, used per instance, which is an array of this structure.</li>
</ul>
<pre class="lang-rust prettyprint-override"><code>pub struct InstanceData {
    pub model: [[f32; 4]; 4],
    pub texture_index: u32,
}
</code></pre>
<ul>
<li>An array of textures provided via a descriptor set</li>
</ul>
<pre class="lang-rust prettyprint-override"><code>let descriptor_binding_flags = [vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT];
let mut descriptorset_layout_binding_flags =
    vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder()
        .binding_flags(&amp;descriptor_binding_flags);

let layout_bindings = [DescriptorSetLayoutBinding::builder()
    .stage_flags(vk::ShaderStageFlags::FRAGMENT)
    .binding(0)
    .descriptor_count(MAX_IMAGES)
    .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
    .build()];

let descriptor_set_layout_info = vk::DescriptorSetLayoutCreateInfo::builder()
    .bindings(&amp;layout_bindings)
    .push_next(&amp;mut descriptorset_layout_binding_flags);

let descriptor_set_layout_texture = unsafe {
    logical_device.create_descriptor_set_layout(&amp;descriptor_set_layout_info, None)
}?;

let descriptor_pool_sizes = [vk::DescriptorPoolSize::builder()
    .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
    .descriptor_count(MAX_IMAGES * 3)
    .build()];

let descriptor_pool_info = vk::DescriptorPoolCreateInfo::builder()
    .pool_sizes(&amp;descriptor_pool_sizes)
    .max_sets(3);

let descriptor_pool =
    unsafe { logical_device.create_descriptor_pool(&amp;descriptor_pool_info, None) }?;

let desc_layouts_texture = vec![descriptor_set_layout_texture; 3];
// TODO: Move this into the texture code to allocate as needed. For now I just hard-code 2 textures.
let mut variable = DescriptorSetVariableDescriptorCountAllocateInfo::builder()
    .descriptor_counts(&amp;[2, 2, 2]);
let descriptor_set_allocate_info = vk::DescriptorSetAllocateInfo::builder()
    .descriptor_pool(descriptor_pool)
    .set_layouts(&amp;desc_layouts_texture)
    .push_next(&amp;mut variable);

let descriptor_sets =
    unsafe { logical_device.allocate_descriptor_sets(&amp;descriptor_set_allocate_info) }?;

let descriptor_set_layouts = [descriptor_set_layout_texture];

let pipelinelayout_info = vk::PipelineLayoutCreateInfo::builder()
    .push_constant_ranges(&amp;push_constant_ranges)
    .set_layouts(&amp;descriptor_set_layouts);

//------------------------------------------------
// Later I copy in the texture info
//------------------------------------------------
let descriptor_image_infos = self.texture_store.get_descriptor_image_info();
let descriptor_writes = [vk::WriteDescriptorSet {
    descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
    dst_set: self.graphics_pipeline.descriptor_sets
        [frame_buffer_info.image_index as usize],
    dst_binding: 0,
    dst_array_element: 0,
    p_image_info: descriptor_image_infos.as_ptr(),
    descriptor_count: 2,
    ..Default::default()
}];

</code></pre>
<p>I then have the following shaders</p>
<pre><code>// vertex
#version 450

layout(push_constant)uniform constants{
    mat4 proj;
}PushConstants;

layout(location=0)in mat4 model;
layout(location=4)in uint tex_id;
layout(location=5)in vec3 position;
layout(location=6)in vec2 uv;
layout(location=7)in vec3 normal;

layout(location=0)out vec2 uv_for_fragment_shader;
layout(location=1)out uint tex_id_for_fragment_shader;

void main(){
    gl_Position=PushConstants.proj*model*vec4(position,1);
    tex_id_for_fragment_shader = tex_id;
    uv_for_fragment_shader=uv;
}

// fragment
#version 450
#extension GL_EXT_nonuniform_qualifier : require

layout(set=0,binding=0)uniform sampler2D tex_samplers[];

layout(location=0)in vec2 uv_from_vertex_shader;
layout(location=1)in flat uint tex_id_from_vertex_shader;

layout(location=0)out vec4 output_colour;

void main(){
    output_colour = texture(tex_samplers[tex_id_from_vertex_shader], uv_from_vertex_shader);
}
</code></pre>
<p>Finally I draw the instances like so</p>
<pre class="lang-rust prettyprint-override"><code>self.logical_device.cmd_push_constants(
    commandbuffer,
    self.graphics_pipeline.layout,
    vk::ShaderStageFlags::VERTEX,
    0,
    &amp;std::mem::transmute::&lt;[[f32; 4]; 4], [u8; 64]&gt;(projection),
);

self.logical_device.cmd_bind_descriptor_sets(
    commandbuffer,
    vk::PipelineBindPoint::GRAPHICS,
    self.graphics_pipeline.layout,
    0,
    &amp;[self.graphics_pipeline.descriptor_sets
        [frame_buffer_info.image_index as usize]],
    &amp;[],
);
self.cube.bind(&amp;self.logical_device, commandbuffer);

self.logical_device.cmd_bind_vertex_buffers(
    commandbuffer,
    VertexBufferBindings::InstanceBuffer as u32,
    &amp;[self.instance_buffer.buffer],
    &amp;[0],
);

self.logical_device.cmd_draw_indexed(
    commandbuffer,
    self.cube.index_count() as u32,
    4,
    0,
    0,
    0,
);
</code></pre>
<p>With that out of the way we can get to the problem at hand, The texture samplers seem to bleed over into the separate instances</p>
<p><a href="https://i.sstatic.net/vyAZq.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/vyAZq.png" alt="2 Cubes with different textures but patches of each-others textures" /></a></p>
<p>I have checked that the texture index itself is consistent across the entire instance by rendering</p>
<pre><code>output_colour = vec4(tex_id_from_vertex_shader, 0,0,1);
</code></pre>
<p>And I get no artefacts</p>
<p><a href="https://i.sstatic.net/N7k30.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/N7k30.png" alt="No artefacts" /></a></p>
<p>So it seems like the sampler itself is hanging around where it shouldn't. Any idea what might be causing this to happen?</p>
<p>If it matters I am on an AMD Radeon 7900 XTX and using the latest drivers</p>

CC BY-SA 4.0
2023-04-15T16:48:52.863Z
I don't know enough about Vulkan to identify the actual problem, but the fact that the errors are in small patches, with rectangular edges, that don't match your geometry is a sign that something is going wrong with the execution of the fragment shader (which is done in small patches like that).
Kevin Reid
CC BY-SA 4.0
2023-04-16T09:50:35.867Z
It seems as though you are correct, its as though the sampler is just locked for the entire fragment. Interestingly enough. Adding a branch in the code like this:

```

if (tex_id_from_vertex_shader == 0) {

        albedo = texture(tex_samplers[0], uv_from_vertex_shader);

    } else {

        albedo = texture(tex_samplers[1], uv_from_vertex_shader);

    }

```

Works just fine. So it is some kind of optimisation?
InvalidOverhead
<texture><vulkan>
Why do texture samplers seem to bleed into other instances when rendering multiple instances of the same mesh with differing textures
InvalidOverhead
# Why do texture samplers seem to bleed into other instances when rendering multiple instances of the same mesh with differing textures

(Asked by: username_0 on 2023-04-14)

I originally asked this question on the gamdev stack exchange but was told by a friend I would be better served here.
Original Question
So I will start off by laying out the relevant structure of my program.
I am using vulkan to render the same mesh over a few instances. To do this I have:

a vertex buffer, used per vertex, which is an array of the following structure

pub struct ShaderVertexData {
    pub position: na::Vector3,
    pub uv: na::Vector2,
    pub normal: na::Vector3,
}


an "instance" buffer, used per instance, which is an array of this structure.

pub struct InstanceData {
    pub model: [[f32; 4]; 4],
    pub texture_index: u32,
}


An array of textures provided via a descriptor set

let descriptor_binding_flags = [vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT];
let mut descriptorset_layout_binding_flags =
    vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder()
        .binding_flags(&descriptor_binding_flags);

let layout_bindings = [DescriptorSetLayoutBinding::builder()
    .stage_flags(vk::ShaderStageFlags::FRAGMENT)
    .binding(0)
    .descriptor_count(MAX_IMAGES)
    .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
    .build()];

let descriptor_set_layout_info = vk::DescriptorSetLayoutCreateInfo::builder()
    .bindings(&layout_bindings)
    .push_next(&mut descriptorset_layout_binding_flags);

let descriptor_set_layout_texture = unsafe {
    logical_device.create_descriptor_set_layout(&descriptor_set_layout_info, None)
}?;

let descriptor_pool_sizes = [vk::DescriptorPoolSize::builder()
    .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
    .descriptor_count(MAX_IMAGES * 3)
    .build()];

let descriptor_pool_info = vk::DescriptorPoolCreateInfo::builder()
    .pool_sizes(&descriptor_pool_sizes)
    .max_sets(3);

let descriptor_pool =
    unsafe { logical_device.create_descriptor_pool(&descriptor_pool_info, None) }?;

let desc_layouts_texture = vec![descriptor_set_layout_texture; 3];
// TODO: Move this into the texture code to allocate as needed. For now I just hard-code 2 textures.
let mut variable = DescriptorSetVariableDescriptorCountAllocateInfo::builder()
    .descriptor_counts(&[2, 2, 2]);
let descriptor_set_allocate_info = vk::DescriptorSetAllocateInfo::builder()
    .descriptor_pool(descriptor_pool)
    .set_layouts(&desc_layouts_texture)
    .push_next(&mut variable);

let descriptor_sets =
    unsafe { logical_device.allocate_descriptor_sets(&descriptor_set_allocate_info) }?;

let descriptor_set_layouts = [descriptor_set_layout_texture];

let pipelinelayout_info = vk::PipelineLayoutCreateInfo::builder()
    .push_constant_ranges(&push_constant_ranges)
    .set_layouts(&descriptor_set_layouts);

//------------------------------------------------
// Later I copy in the texture info
//------------------------------------------------
let descriptor_image_infos = self.texture_store.get_descriptor_image_info();
let descriptor_writes = [vk::WriteDescriptorSet {
    descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
    dst_set: self.graphics_pipeline.descriptor_sets
        [frame_buffer_info.image_index as usize],
    dst_binding: 0,
    dst_array_element: 0,
    p_image_info: descriptor_image_infos.as_ptr(),
    descriptor_count: 2,
    ..Default::default()
}];


I then have the following shaders
// vertex
#version 450

layout(push_constant)uniform constants{
    mat4 proj;
}PushConstants;

layout(location=0)in mat4 model;
layout(location=4)in uint tex_id;
layout(location=5)in vec3 position;
layout(location=6)in vec2 uv;
layout(location=7)in vec3 normal;

layout(location=0)out vec2 uv_for_fragment_shader;
layout(location=1)out uint tex_id_for_fragment_shader;

void main(){
    gl_Position=PushConstants.proj*model*vec4(position,1);
    tex_id_for_fragment_shader = tex_id;
    uv_for_fragment_shader=uv;
}

// fragment
#version 450
#extension GL_EXT_nonuniform_qualifier : require

layout(set=0,binding=0)uniform sampler2D tex_samplers[];

layout(location=0)in vec2 uv_from_vertex_shader;
layout(location=1)in flat uint tex_id_from_vertex_shader;

layout(location=0)out vec4 output_colour;

void main(){
    output_colour = texture(tex_samplers[tex_id_from_vertex_shader], uv_from_vertex_shader);
}

Finally I draw the instances like so
self.logical_device.cmd_push_constants(
    commandbuffer,
    self.graphics_pipeline.layout,
    vk::ShaderStageFlags::VERTEX,
    0,
    &std::mem::transmute::<[[f32; 4]; 4], [u8; 64]>(projection),
);

self.logical_device.cmd_bind_descriptor_sets(
    commandbuffer,
    vk::PipelineBindPoint::GRAPHICS,
    self.graphics_pipeline.layout,
    0,
    &[self.graphics_pipeline.descriptor_sets
        [frame_buffer_info.image_index as usize]],
    &[],
);
self.cube.bind(&self.logical_device, commandbuffer);

self.logical_device.cmd_bind_vertex_buffers(
    commandbuffer,
    VertexBufferBindings::InstanceBuffer as u32,
    &[self.instance_buffer.buffer],
    &[0],
);

self.logical_device.cmd_draw_indexed(
    commandbuffer,
    self.cube.index_count() as u32,
    4,
    0,
    0,
    0,
);

With that out of the way we can get to the problem at hand, The texture samplers seem to bleed over into the separate instances

I have checked that the texture index itself is consistent across the entire instance by rendering
output_colour = vec4(tex_id_from_vertex_shader, 0,0,1);

And I get no artefacts

So it seems like the sampler itself is hanging around where it shouldn't. Any idea what might be causing this to happen?
If it matters I am on an AMD Radeon 7900 XTX and using the latest drivers

**Tags:** texture, vulkan
> **Comments:**
> **username_1**: I don't know enough about Vulkan to identify the actual problem, but the fact that the errors are in small patches, with rectangular edges, that don't match your geometry is a sign that something is going wrong with the execution of the fragment shader (which is done in small patches like that).  
> **username_0**: It seems as though you are correct, its as though the sampler is just locked for the entire fragment. Interestingly enough. Adding a branch in the code like this:  ```  if (tex_id_from_vertex_shader == 0) {          albedo = texture(tex_samplers[0], uv_from_vertex_shader);      } else {          albedo = texture(tex_samplers[1], uv_from_vertex_shader);      }  ```  Works just fine. So it is some kind of optimisation?  

---

## 1 answers

### Answer by username_0 on 2023-04-19. Score: 2

I finally got an answer on the AMD Forums.
The issue was that you need to tell the GPU when you plan on indexing resource arrays with non uniform values.
My shader now looks like this:
#version 450
#extension GL_EXT_nonuniform_qualifier : require

layout(set=0,binding=0)uniform sampler2D tex_samplers[];

layout(location=0)in vec2 uv_from_vertex_shader;
layout(location=1)in vec3 normal_from_vertex_shader;
layout(location=2)in flat uint tex_id_from_vertex_shader;


layout(location=0)out vec4 output_colour;

void main(){
    vec4 albedo = texture(tex_samplers[nonuniformEXT(tex_id_from_vertex_shader)], uv_from_vertex_shader);
    float light = clamp(dot(normal_from_vertex_shader, normalize(vec3(1,1,1))), 0.2, 1);
    output_colour =  vec4(albedo.rgb * light, albedo.a);
}

The important part is the nonuniformEXT that wraps the index
It is also worth mentioning that technically this also requires that you enable shaderSampledImageArrayNonUniformIndexing on your VkPhysicalDeviceDescriptorIndexingFeatures

> **Comments:**
> **username_2**: Do you have the validation layers turned on? This error should have been reported by the layers.  
> **username_0**: Yeah I do have them enabled. Perhaps there is some specific config needed to catch this flavour of error but this didn't show up for me  
> **username_2**: In the Vulkan Configurator under "Validation" in  VK_LAYER_KHRONOS_validation->Validation Areas->Shader-Based->GPU-Assisted->Check descriptor indexing accesses Should trigger the error.  

---

