I originally asked this question on the gamdev stack exchange but was told by a friend I would be better served here.
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
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