0
\$\begingroup\$

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 = 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

2 Cubes with different textures but patches of each-others textures

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

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

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .