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