<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](https://gamedev.stackexchange.com/questions/205276/why-do-texture-samplers-seem-to-bleed-into-other-instances-when-rendering-multip)

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

```rust
pub struct ShaderVertexData {
    pub position: na::Vector3<f32>,
    pub uv: na::Vector2<f32>,
    pub normal: na::Vector3<f32>,
}

```

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

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

```

* An array of textures provided via a descriptor set

```rust
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

```rust
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

[![2 Cubes with different textures but patches of each-others textures](https://i.sstatic.net/vyAZq.png)](https://i.sstatic.net/vyAZq.png)

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

[![No artefacts](https://i.sstatic.net/N7k30.png)](https://i.sstatic.net/N7k30.png)

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](https://community.amd.com/t5/opengl-vulkan/vulkan-fragment-shader-bug-when-using-array-of-textures/m-p/600999/thread-id/4659#M4661).

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.  

---

