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:
pub struct ShaderVertexData { pub position: na::Vector3<f32>, pub uv: na::Vector2<f32>, pub normal: na::Vector3<f32>, }
pub struct InstanceData { pub model: [[f32; 4]; 4], pub texture_index: u32, }
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-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 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
Solved! Go to 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
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.
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
Yes that is exactly what I was missing. Adding that in fixes the issue.