3
votes

I'm trying to create a single pipeline with a layout where it requires two bindings, a dynamic UBO and a image/sampler binding. I want each binding to come from a separate descriptor set, so I'd bind two descriptor sets per draw call. One descriptor set is for texture per object, the other is for the dynamic UBO(shared between objects). I want to be able to do something like this in the rendering portion:


commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);

for (int ii = 0; ii < mActiveQuads; ii++)
{
    uint32_t dynamicOffset = ii * static_cast<uint32_t>(dynamicAlignment);

    // bind texture for this quad
    commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, sharedPipelineLayout, 0, 1,
            &swapResources[current_buffer].textureDescriptors[ii], 1, &dynamicOffset);

    // draw the dynamic UBO with offset for this quad
    commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, sharedPipelineLayout, 0, 1,
                &swapResources[current_buffer].quadDescriptor, 1, &dynamicOffset);

    commandBuffer.draw(2 * 3, 1, 0, 0);
}

But this doesn't seem to work. First of all I'm not sure I have understood everything about descriptor sets and pipeline layouts to know if what I'm doing is allowed. Does this even make sense? That I can create a pipeline with a 2 binding layout, but create each descriptor to fill just one of those bindings each, then bind two descriptors per draw call for that pipeline?

If it is allowed. This is how I'm creating the pipeline and descriptors:

vk::DescriptorSetLayoutBinding const layout_bindings[2] = { vk::DescriptorSetLayoutBinding()
        .setBinding(0)
        .setDescriptorType(vk::DescriptorType::eUniformBufferDynamic)
        .setDescriptorCount(1)
        .setStageFlags(vk::ShaderStageFlagBits::eVertex)
        .setPImmutableSamplers(nullptr),
        vk::DescriptorSetLayoutBinding()
        .setBinding(1)
        .setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
        .setDescriptorCount(1)//texture_count)
        .setStageFlags(vk::ShaderStageFlagBits::eFragment)
        .setPImmutableSamplers(nullptr) };


    // note binding count is 1 here
    auto const descriptor_layout = vk::DescriptorSetLayoutCreateInfo().setBindingCount(1).setPBindings(&layout_bindings[0]); // using the first part of the above layout
    device.createDescriptorSetLayout(&descriptor_layout, nullptr, &quadDescriptorLayout);

    // note binding count is 1 here
    auto const descriptor_layout2 = vk::DescriptorSetLayoutCreateInfo().setBindingCount(1).setPBindings(&layout_bindings[1]); // using the second part of the above layout
    device.createDescriptorSetLayout(&descriptor_layout2, nullptr, &textureDescriptorLayout);

    // Now create the pipeline, note we use both the bindings above with
    // layout count = 2
    auto const pPipelineLayoutCreateInfo = vk::PipelineLayoutCreateInfo().setSetLayoutCount(2).setPSetLayouts(desc_layout);
    device.createPipelineLayout(&pPipelineLayoutCreateInfo, nullptr, &sharedPipelineLayout);

and the descriptors themselves:

    // alloc quad descriptor
    alloc_info =
        vk::DescriptorSetAllocateInfo()
            .setDescriptorPool(desc_pool)
            .setDescriptorSetCount(1)
            .setPSetLayouts(&quadDescriptorLayout);


     // texture descriptors(multiple descriptors, one per quad object)
    alloc_info =
        vk::DescriptorSetAllocateInfo()
            .setDescriptorPool(desc_pool)
            .setDescriptorSetCount(1)
            .setPSetLayouts(&textureDescriptorLayout);

Previously, with the texture and UBO in a single descriptor set it worked fine, I could see multiple quads but all sharing a single texture. When I split off the textures into a different descriptor set, that's when I get a hanging app. I get a 'device lost' error on trying to submit the graphics queue.

Any insight as to if this is possible to do or if I'm doing something wrong in my setup would be very appreciated. Thank you very much!

Below adding Shader code:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 0) uniform UniformBufferObject {
    mat4 mvp;
    vec4 position[6];
    vec4 attr[6];
} ubo;


layout(location = 0) out vec2 fragTexCoord;

void main() {
    gl_Position = ubo.mvp *ubo.position[gl_VertexIndex];
    fragTexCoord = vec2(ubo.attr[gl_VertexIndex].x, ubo.attr[gl_VertexIndex].y);
}

Pixel shader:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(set=0, binding = 1) uniform sampler2D texSampler;

layout(location = 0) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(texSampler, fragTexCoord);
}
1
What does your validation layer say? What does your shader say?Nicol Bolas
Hi Nicol thank you, I added the shader but I think Jesse got the problem below. The validation layer did not say anything.Marvg
@Marvg Question : Why do you pass &dynamicOffset also into bindDescriptorSets for textures ?Michael IV

1 Answers

5
votes

Yes, you can do this. Your pipeline layout has two descriptor sets. Each of the two descriptor set layouts has one descriptor: a dynamic UBO and a texture. At draw time, you bind one descriptor set of each descriptor set layout to the appropriate set number.

It looks like the firstSet parameter when binding the texture descriptor set is wrong: that's the second set in the pipeline layout, so it has index 1, but you're passing 0. The validation layers should have warned you that you're binding a descriptor set with a set layout that doesn't match what the pipeline layout expects for that set.

You don't show the shader code that accesses these, so you may have done this. But when going from a single descriptor set to two descriptor sets, you need to update the set index in the sampler binding.