cancel
Showing results for 
Search instead for 
Did you mean: 

OpenGL & Vulkan

InvalidOverhead
Journeyman III

Vulkan fragment shader bug when using array of textures

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<f32>,
    pub uv: na::Vector2<f32>,
    pub normal: na::Vector3<f32>,
}
  • 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 = 
    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 = 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 =
    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

Red texture appearing on white cubes and vice-versaRed texture appearing on white cubes and vice-versa

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

Output texture index to red channel, no artifactsOutput texture index to red channel, no artifacts

I have also tried adding a hard-coded branch into my shader instead of using the texture index directly and that works just fine:

 

 

 

 

vec4 albedo;
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);
}

 

 

 

 


So it seems like the sampler itself is hanging around where it shouldn't. And it seems to be because of some incorrect assumption the driver makes. I have tried this on an Nvidia card and have no issues.

I am on an AMD Radeon 7900 XTX and using the latest drivers

0 Likes
1 Solution

Hi,

I've created internal ticket to track this issue: SWDEV-395003.
Meanwhile, have you tried changing:

tex_samplers[tex_id_from_vertex_shader]
to:
tex_samplers[nonuniform(tex_id_from_vertex_shader)]

Thanks,

Owen

View solution in original post

0 Likes
3 Replies
dipak
Big Boss

Hi @InvalidOverhead ,

Thank you for reporting it. I have whitelisted you and moved the post to the Vulkan forum.

I will forward the issue to the Vulkan team.

Thanks.

 

0 Likes

Hi,

I've created internal ticket to track this issue: SWDEV-395003.
Meanwhile, have you tried changing:

tex_samplers[tex_id_from_vertex_shader]
to:
tex_samplers[nonuniform(tex_id_from_vertex_shader)]

Thanks,

Owen

0 Likes

Yes that is exactly what I was missing. Adding that in fixes the issue.

0 Likes