Why am I getting this error when creating descriptor sets after adding a descriptor slot to my descriptor set layout

here is the code where I get the error (on the call to vkUpdateDescriptorSets):

void createDescriptorSets() {
        std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
        VkDescriptorSetAllocateInfo allocInfo{};
        allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        allocInfo.descriptorPool = descriptorPool;
        allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        allocInfo.pSetLayouts = layouts.data();

        descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }

        for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            VkDescriptorBufferInfo bufferInfo{};
            bufferInfo.buffer = uniformBuffers[i];
            bufferInfo.offset = 0;
            bufferInfo.range = sizeof(UniformBufferObject);

            VkDescriptorBufferInfo jointBufferInfo{};
            jointBufferInfo.buffer = jointUniformBuffers[i];
            jointBufferInfo.offset = 0;
            jointBufferInfo.range = sizeof(JointUniformBufferObject);

            VkDescriptorImageInfo imageInfo{};
            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
            imageInfo.imageView = textureImageView;
            imageInfo.sampler = textureSampler;

            std::array<VkWriteDescriptorSet, 3> descriptorWrites{};

            descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[0].dstSet = descriptorSets[i];
            descriptorWrites[0].dstBinding = 0;
            descriptorWrites[0].dstArrayElement = 0;
            descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[0].descriptorCount = 1;
            descriptorWrites[0].pBufferInfo = &bufferInfo;

            descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[1].dstSet = descriptorSets[i];
            descriptorWrites[1].dstBinding = 1;
            descriptorWrites[1].dstArrayElement = 0;
            descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[1].descriptorCount = 1;
            descriptorWrites[1].pBufferInfo = &jointBufferInfo;

            descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[2].dstSet = descriptorSets[i];
            descriptorWrites[2].dstBinding = 2;
            descriptorWrites[2].dstArrayElement = 0;
            descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
            descriptorWrites[2].descriptorCount = 1;
            descriptorWrites[2].pImageInfo = &imageInfo;

            vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
        }
    }

for creating descriptor set layouts:
void createDescriptorSetLayout() {
        VkDescriptorSetLayoutBinding uboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding jointUboLayoutBinding{};
uboLayoutBinding.binding = 1;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.pImmutableSamplers = nullptr;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding samplerLayoutBinding{};
        samplerLayoutBinding.binding = 2;
        samplerLayoutBinding.descriptorCount = 1;
        samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        samplerLayoutBinding.pImmutableSamplers = nullptr;
        samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

        std::array<VkDescriptorSetLayoutBinding, 3> bindings = {uboLayoutBinding, jointUboLayoutBinding, samplerLayoutBinding};
        VkDescriptorSetLayoutCreateInfo layoutInfo{};
        layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
        layoutInfo.pBindings = bindings.data();

        if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }
    }

for creating descriptor pools:
void createDescriptorPool() {
        std::array<VkDescriptorPoolSize, 3> poolSizes{};
        poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        poolSizes[2].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        poolSizes[2].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);

        VkDescriptorPoolCreateInfo poolInfo{};
        poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
        poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
        poolInfo.pPoolSizes = poolSizes.data();
        poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);

        if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor pool!");
        }
    }

here is where I call the functions:

void initVulkan() {
        createInstance();
        setupDebugMessenger();
        createSurface();
        pickPhysicalDevice();
        createLogicalDevice();
        createSwapChain();
        createImageViews();
        createRenderPass();
        createDescriptorSetLayout();
        createGraphicsPipeline();
        createCommandPool();
        createColorResources();
        createDepthResources();
        createFramebuffers();
        createTextureImage();
        createTextureImageView();
        createTextureSampler();
        loadModel();
        createVertexBuffer();
        createIndexBuffer();
        createUniformBuffers();
        createDescriptorPool();
        createDescriptorSets();
        createCommandBuffers();
        createSyncObjects();
    }

I call this function only once when my program starts up. My program works correctly until I add this to my bindings:

VkDescriptorSetLayoutBinding jointUboLayoutBinding{};
uboLayoutBinding.binding = 1;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.pImmutableSamplers = nullptr;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

and I add the binding in the vertex shader:

#version 450

layout(binding = 0) uniform UniformBufferObject {
    mat4 model;
    mat4 view;
    mat4 proj;
} ubo;

layout(set = 1, binding = 1) uniform JointsUBO {
    mat4 bones[100];
} joints;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

The error message:

Validation Error: [ VUID-VkGraphicsPipelineCreateInfo-layout-00756 ] Object 0: handle = 0xd5b26f0000000010, type = VK_OBJECT_TYPE_SHADER_MODULE; Object 1: handle = 0xdcc8fd0000000012, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0x45717876 | vkCreateGraphicsPipelines(): pCreateInfos[0] Set 0 Binding 0 in shader (VK_SHADER_STAGE_VERTEX_BIT) uses descriptor slot but descriptor not accessible from stage VK_SHADER_STAGE_VERTEX_BIT The Vulkan spec states: layout must be consistent with all shaders specified in pStages
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x2723ba0000000037, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[0] failed write update validation for VkDescriptorSet 0x2723ba0000000037[] with error: VkDescriptorSet 0x2723ba0000000037[] allocated with VkDescriptorSetLayout 0xe7e6d0000000000f[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount 
Segmentation fault (core dumped)

The shader says that it has two uniform buffers; one of them in set 0 (the default if you don’t specify it) and the other in set 1. Your descriptor set layout says that there is one descriptor set with 3 descriptors: two uniform buffers and a sampler.

These are incompatible with one another.

How should I change my descriptor set layout? I understand I need 3 descriptor sets in my layout but how do I do that?

Well, it’s unclear to me the thinking behind how you allocated your descriptor sets.

I can understand wanting the projection and view matrices to be in set 0, since they don’t change per-object (and therefore you can binding set 1 for various objects without changing set 0). But the model matrix does change per-object. So how do you intend to change that without also changing set 0? And if you’re binding both set 0 and set 1 per-object… why are they separate descriptor sets?

Also, a descriptor set layout defines the layout for a descriptor set. Singular. If your pipeline uses multiple descriptor sets, then the pipeline layout needs to have multiple descriptor set layouts.

Lastly, why does JointsUBO use binding 1? What is in set 1, binding 0 that it needs to use binding 1?

Overall, it’s not clear to me that you understand what a descriptor set is intended to mean, conceptually.

I understand now that I should have separated the model matrix from the view and projection matrices so that I can update it per object when rendering without incurring the overhead of having to also send the projection and view matrices whenever I update the model matrix. I’ve updated my code; I allocate enough descriptor sets so that when I use VkWriteDescriptorSets I’m not overwriting the descriptor sets like I was before so I have a different dstSet for each VkWriteDescriptorSet I’m not sure if this was a problem?

So my pipeline needs a descriptor set layout for each descriptor set?

here is my updated code I am still getting the same error:

void createDescriptorSets() {
        std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
        VkDescriptorSetAllocateInfo allocInfo{};
        allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        allocInfo.descriptorPool = descriptorPool;
        allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        allocInfo.pSetLayouts = layouts.data();

        descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        modelDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT); // Added vectors to hold descriptor sets for each binding with a unique descriptor set number (set = x). Needed to allocate a descriptor set for each binding?
        jointDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        samplerDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &allocInfo, modelDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &allocInfo, jointDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &allocInfo, samplerDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }

        for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            VkDescriptorBufferInfo bufferInfo{};
            bufferInfo.buffer = uniformBuffers[i];
            bufferInfo.offset = 0;
            bufferInfo.range = sizeof(UniformBufferObject);

            VkDescriptorBufferInfo modelBufferInfo{};
            modelBufferInfo.buffer = modelUniformBuffers[i];
            modelBufferInfo.offset = 0;
            modelBufferInfo.range = sizeof(ModelUniformBufferObject);

            VkDescriptorBufferInfo jointBufferInfo{};
            jointBufferInfo.buffer = jointUniformBuffers[i];
            jointBufferInfo.offset = 0;
            jointBufferInfo.range = sizeof(JointUniformBufferObject);

            VkDescriptorImageInfo imageInfo{};
            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
            imageInfo.imageView = textureImageView;
            imageInfo.sampler = textureSampler;

            std::array<VkWriteDescriptorSet, 4> descriptorWrites{};

            descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[0].dstSet = descriptorSets[i];
            descriptorWrites[0].dstBinding = 0;
            descriptorWrites[0].dstArrayElement = 0;
            descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[0].descriptorCount = 1;
            descriptorWrites[0].pBufferInfo = &bufferInfo;

            descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[1].dstSet = modelDescriptorSets[i];
            descriptorWrites[1].dstBinding = 1;
            descriptorWrites[1].dstArrayElement = 0;
            descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[1].descriptorCount = 1;
            descriptorWrites[1].pBufferInfo = &modelBufferInfo;

            descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[2].dstSet = jointDescriptorSets[i];
            descriptorWrites[2].dstBinding = 2;
            descriptorWrites[2].dstArrayElement = 0;
            descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[2].descriptorCount = 1;
            descriptorWrites[2].pBufferInfo = &jointBufferInfo;

            descriptorWrites[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[3].dstSet = samplerDescriptorSets[i];
            descriptorWrites[3].dstBinding = 3;
            descriptorWrites[3].dstArrayElement = 0;
            descriptorWrites[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
            descriptorWrites[3].descriptorCount = 1;
            descriptorWrites[3].pImageInfo = &imageInfo;

            vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
        }
    }
void createDescriptorSetLayout() {
        VkDescriptorSetLayoutBinding uboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding modelUboLayoutBinding{};
        modelUboLayoutBinding.binding = 1;
        modelUboLayoutBinding.descriptorCount = 1;
        modelUboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        modelUboLayoutBinding.pImmutableSamplers = nullptr;
        modelUboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding jointUboLayoutBinding{};
        jointUboLayoutBinding.binding = 2;
        jointUboLayoutBinding.descriptorCount = 1;
        jointUboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        jointUboLayoutBinding.pImmutableSamplers = nullptr;
        jointUboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding samplerLayoutBinding{};
        samplerLayoutBinding.binding = 3;
        samplerLayoutBinding.descriptorCount = 1;
        samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        samplerLayoutBinding.pImmutableSamplers = nullptr;
        samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

        std::array<VkDescriptorSetLayoutBinding, 4> bindings = {uboLayoutBinding, modelUboLayoutBinding, jointUboLayoutBinding, samplerLayoutBinding};
        VkDescriptorSetLayoutCreateInfo layoutInfo{};
        layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
        layoutInfo.pBindings = bindings.data();

        if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }
    }
void createDescriptorPool() {
        std::array<VkDescriptorPoolSize, 4> poolSizes{};
        poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        poolSizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        poolSizes[2].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        poolSizes[3].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        poolSizes[3].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);

        VkDescriptorPoolCreateInfo poolInfo{};
        poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
        poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
        poolInfo.pPoolSizes = poolSizes.data();
        poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT)*4; // Wasn't allocating enough descriptor sets?

        if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor pool!");
        }
    }
#version 450

layout(set = 0, binding = 0) uniform UniformBufferObject {
    mat4 view;
    mat4 proj;
} ubo;

layout(set = 1, binding = 1) uniform ModelUniformBufferObject {
    mat4 model;
} modelUbo;

layout(set = 2, binding = 2) uniform JointsUBO {
    mat4 bones[100];
} joints;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
    gl_Position = ubo.proj * ubo.view * modelUbo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}
#version 450

layout(set = 3, binding = 3) uniform sampler2D texSampler;

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(texSampler, fragTexCoord);
}

Oh, so each descriptor set layout specifies the descriptors for only 1 descriptor set? So I would need to create 3 more descriptor set layouts and pass them as an array to the pipeline where the first in the array has the descriptors for the first descriptor set and the second layout for the second descriptor set and so on?

What is this? Why do you keep increasing the binding count as if different sets occupy the same binding namespace? Because they don’t. set 0, binding 0 and set 1, binding 0 are different bindings because they live in different sets.

you mean I dont need to change the binding number if they use different sets?

I changed them all to binding 0 because they are all different sets but it’s still not working here’s my updated code:

void createDescriptorSets() {
        std::vector<VkDescriptorSetLayout> viewProjectionLayouts(MAX_FRAMES_IN_FLIGHT, viewProjectionDescriptorSetLayout);
        std::vector<VkDescriptorSetLayout> modelLayouts(MAX_FRAMES_IN_FLIGHT, modelDescriptorSetLayout);
        std::vector<VkDescriptorSetLayout> jointLayouts(MAX_FRAMES_IN_FLIGHT, jointDescriptorSetLayout);
        std::vector<VkDescriptorSetLayout> samplerLayouts(MAX_FRAMES_IN_FLIGHT, samplerDescriptorSetLayout);
        VkDescriptorSetAllocateInfo vpAllocInfo{};
        vpAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        vpAllocInfo.descriptorPool = descriptorPool;
        vpAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        vpAllocInfo.pSetLayouts = viewProjectionLayouts.data();

        VkDescriptorSetAllocateInfo modelAllocInfo{};
        modelAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        modelAllocInfo.descriptorPool = descriptorPool;
        modelAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        modelAllocInfo.pSetLayouts = modelLayouts.data();

        VkDescriptorSetAllocateInfo jointAllocInfo{};
        jointAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        jointAllocInfo.descriptorPool = descriptorPool;
        jointAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        jointAllocInfo.pSetLayouts = jointLayouts.data();

        VkDescriptorSetAllocateInfo samplerAllocInfo{};
        samplerAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        samplerAllocInfo.descriptorPool = descriptorPool;
        samplerAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        samplerAllocInfo.pSetLayouts = samplerLayouts.data();

        descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        modelDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT); // Added vectors to hold descriptor sets for each binding with a unique descriptor set number (set = x). Needed to allocate a descriptor set for each binding?
        jointDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        samplerDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        if (vkAllocateDescriptorSets(device, &vpAllocInfo, descriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &modelAllocInfo, modelDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &jointAllocInfo, jointDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &samplerAllocInfo, samplerDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }

        for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            VkDescriptorBufferInfo bufferInfo{};
            bufferInfo.buffer = uniformBuffers[i];
            bufferInfo.offset = 0;
            bufferInfo.range = sizeof(UniformBufferObject);

            VkDescriptorBufferInfo modelBufferInfo{};
            modelBufferInfo.buffer = modelUniformBuffers[i];
            modelBufferInfo.offset = 0;
            modelBufferInfo.range = sizeof(ModelUniformBufferObject);

            VkDescriptorBufferInfo jointBufferInfo{};
            jointBufferInfo.buffer = jointUniformBuffers[i];
            jointBufferInfo.offset = 0;
            jointBufferInfo.range = sizeof(JointUniformBufferObject);

            VkDescriptorImageInfo imageInfo{};
            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
            imageInfo.imageView = textureImageView;
            imageInfo.sampler = textureSampler;

            std::array<VkWriteDescriptorSet, 4> descriptorWrites{};

            descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[0].dstSet = descriptorSets[i];
            descriptorWrites[0].dstBinding = 0;
            descriptorWrites[0].dstArrayElement = 0;
            descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[0].descriptorCount = 1;
            descriptorWrites[0].pBufferInfo = &bufferInfo;

            descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[1].dstSet = modelDescriptorSets[i];
            descriptorWrites[1].dstBinding = 0;
            descriptorWrites[1].dstArrayElement = 0;
            descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[1].descriptorCount = 1;
            descriptorWrites[1].pBufferInfo = &modelBufferInfo;

            descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[2].dstSet = jointDescriptorSets[i];
            descriptorWrites[2].dstBinding = 0;
            descriptorWrites[2].dstArrayElement = 0;
            descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[2].descriptorCount = 1;
            descriptorWrites[2].pBufferInfo = &jointBufferInfo;

            descriptorWrites[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[3].dstSet = samplerDescriptorSets[i];
            descriptorWrites[3].dstBinding = 0;
            descriptorWrites[3].dstArrayElement = 0;
            descriptorWrites[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
            descriptorWrites[3].descriptorCount = 1;
            descriptorWrites[3].pImageInfo = &imageInfo;

            vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
        }
    }
void createDescriptorSetLayouts() {
        VkDescriptorSetLayoutBinding uboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding modelUboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding jointUboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding samplerLayoutBinding{};
        samplerLayoutBinding.binding = 0;
        samplerLayoutBinding.descriptorCount = 1;
        samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        samplerLayoutBinding.pImmutableSamplers = nullptr;
        samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

        std::array<VkDescriptorSetLayoutBinding, 1> bindings1 = {uboLayoutBinding};
        VkDescriptorSetLayoutCreateInfo vpLayoutInfo{};
        vpLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        vpLayoutInfo.bindingCount = static_cast<uint32_t>(bindings1.size());
        vpLayoutInfo.pBindings = bindings1.data();

        if (vkCreateDescriptorSetLayout(device, &vpLayoutInfo, nullptr, &viewProjectionDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }

        std::array<VkDescriptorSetLayoutBinding, 1> bindings2 = {modelUboLayoutBinding};
        VkDescriptorSetLayoutCreateInfo modelLayoutInfo{};
        modelLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        modelLayoutInfo.bindingCount = static_cast<uint32_t>(bindings2.size());
        modelLayoutInfo.pBindings = bindings2.data();

        if (vkCreateDescriptorSetLayout(device, &modelLayoutInfo, nullptr, &modelDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }

        std::array<VkDescriptorSetLayoutBinding, 1> bindings3 = {jointUboLayoutBinding};
        VkDescriptorSetLayoutCreateInfo jointLayoutInfo{};
        jointLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        jointLayoutInfo.bindingCount = static_cast<uint32_t>(bindings3.size());
        jointLayoutInfo.pBindings = bindings3.data();

        if (vkCreateDescriptorSetLayout(device, &jointLayoutInfo, nullptr, &jointDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }

        std::array<VkDescriptorSetLayoutBinding, 1> bindings4 = {samplerLayoutBinding};
        VkDescriptorSetLayoutCreateInfo samplerLayoutInfo{};
        samplerLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        samplerLayoutInfo.bindingCount = static_cast<uint32_t>(bindings4.size());
        samplerLayoutInfo.pBindings = bindings4.data();

        if (vkCreateDescriptorSetLayout(device, &samplerLayoutInfo, nullptr, &samplerDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }
    }
#version 450

layout(set = 0, binding = 0) uniform UniformBufferObject {
    mat4 view;
    mat4 proj;
} ubo;

layout(set = 1, binding = 0) uniform ModelUniformBufferObject {
    mat4 model;
} modelUbo;

layout(set = 2, binding = 0) uniform JointsUBO {
    mat4 bones[100];
} joints;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
    gl_Position = ubo.proj * ubo.view * modelUbo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}
#version 450

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

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(texSampler, fragTexCoord);
}

the descriptor pool is the same but I now have MAX_FRAMES_IN_FLIGHT descriptor layouts per descriptor set so I updated the pipeline creation code:

VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
        std::vector<VkDescriptorSetLayout> layouts;
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(viewProjectionDescriptorSetLayout);
        }
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(modelDescriptorSetLayout);
        }
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(jointDescriptorSetLayout);
        }
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(samplerDescriptorSetLayout);
        }
        pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
        pipelineLayoutInfo.setLayoutCount = MAX_FRAMES_IN_FLIGHT*4;
        pipelineLayoutInfo.pSetLayouts = layouts.data();

        if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create pipeline layout!");
        }

It’s still not working :confused: here’s the error:

validation layer: Validation Error: [ VUID-VkGraphicsPipelineCreateInfo-layout-00756 ] Object 0: handle = 0x9fde6b0000000014, type = VK_OBJECT_TYPE_SHADER_MODULE; Object 1: handle = 0xdd3a8a0000000015, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0x45717876 | vkCreateGraphicsPipelines(): pCreateInfos[0] Set 3 Binding 0 in shader (VK_SHADER_STAGE_FRAGMENT_BIT) uses descriptor slot but descriptor not accessible from stage VK_SHADER_STAGE_FRAGMENT_BIT The Vulkan spec states: layout must be consistent with all shaders specified in pStages 
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x56c9bd0000000040, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[1] failed write update validation for VkDescriptorSet 0x56c9bd0000000040[] with error: VkDescriptorSet 0x56c9bd0000000040[] allocated with VkDescriptorSetLayout 0xd5b26f0000000010[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount 
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x89e60f0000000042, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[2] failed write update validation for VkDescriptorSet 0x89e60f0000000042[] with error: VkDescriptorSet 0x89e60f0000000042[] allocated with VkDescriptorSetLayout 0x980f360000000011[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount 
Segmentation fault (core dumped)

I changed all the binding numbers to 0 and I created a descriptor set layout for each descriptor set here is my updated code:

void createDescriptorSets() {
        std::vector<VkDescriptorSetLayout> viewProjectionLayouts(MAX_FRAMES_IN_FLIGHT, viewProjectionDescriptorSetLayout);
        std::vector<VkDescriptorSetLayout> modelLayouts(MAX_FRAMES_IN_FLIGHT, modelDescriptorSetLayout);
        std::vector<VkDescriptorSetLayout> jointLayouts(MAX_FRAMES_IN_FLIGHT, jointDescriptorSetLayout);
        std::vector<VkDescriptorSetLayout> samplerLayouts(MAX_FRAMES_IN_FLIGHT, samplerDescriptorSetLayout);
        VkDescriptorSetAllocateInfo vpAllocInfo{};
        vpAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        vpAllocInfo.descriptorPool = descriptorPool;
        vpAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        vpAllocInfo.pSetLayouts = viewProjectionLayouts.data();

        VkDescriptorSetAllocateInfo modelAllocInfo{};
        modelAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        modelAllocInfo.descriptorPool = descriptorPool;
        modelAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        modelAllocInfo.pSetLayouts = modelLayouts.data();

        VkDescriptorSetAllocateInfo jointAllocInfo{};
        jointAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        jointAllocInfo.descriptorPool = descriptorPool;
        jointAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        jointAllocInfo.pSetLayouts = jointLayouts.data();

        VkDescriptorSetAllocateInfo samplerAllocInfo{};
        samplerAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
        samplerAllocInfo.descriptorPool = descriptorPool;
        samplerAllocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
        samplerAllocInfo.pSetLayouts = samplerLayouts.data();

        descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        modelDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT); // Added vectors to hold descriptor sets for each binding with a unique descriptor set number (set = x). Needed to allocate a descriptor set for each binding?
        jointDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        samplerDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
        if (vkAllocateDescriptorSets(device, &vpAllocInfo, descriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &modelAllocInfo, modelDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &jointAllocInfo, jointDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        if (vkAllocateDescriptorSets(device, &samplerAllocInfo, samplerDescriptorSets.data()) != VK_SUCCESS) {
            throw std::runtime_error("failed to allocate descriptor sets!");
        }

        for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            VkDescriptorBufferInfo bufferInfo{};
            bufferInfo.buffer = uniformBuffers[i];
            bufferInfo.offset = 0;
            bufferInfo.range = sizeof(UniformBufferObject);

            VkDescriptorBufferInfo modelBufferInfo{};
            modelBufferInfo.buffer = modelUniformBuffers[i];
            modelBufferInfo.offset = 0;
            modelBufferInfo.range = sizeof(ModelUniformBufferObject);

            VkDescriptorBufferInfo jointBufferInfo{};
            jointBufferInfo.buffer = jointUniformBuffers[i];
            jointBufferInfo.offset = 0;
            jointBufferInfo.range = sizeof(JointUniformBufferObject);

            VkDescriptorImageInfo imageInfo{};
            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
            imageInfo.imageView = textureImageView;
            imageInfo.sampler = textureSampler;

            std::array<VkWriteDescriptorSet, 4> descriptorWrites{};

            descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[0].dstSet = descriptorSets[i];
            descriptorWrites[0].dstBinding = 0;
            descriptorWrites[0].dstArrayElement = 0;
            descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[0].descriptorCount = 1;
            descriptorWrites[0].pBufferInfo = &bufferInfo;

            descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[1].dstSet = modelDescriptorSets[i];
            descriptorWrites[1].dstBinding = 0;
            descriptorWrites[1].dstArrayElement = 0;
            descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[1].descriptorCount = 1;
            descriptorWrites[1].pBufferInfo = &modelBufferInfo;

            descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[2].dstSet = jointDescriptorSets[i];
            descriptorWrites[2].dstBinding = 0;
            descriptorWrites[2].dstArrayElement = 0;
            descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[2].descriptorCount = 1;
            descriptorWrites[2].pBufferInfo = &jointBufferInfo;

            descriptorWrites[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[3].dstSet = samplerDescriptorSets[i];
            descriptorWrites[3].dstBinding = 0;
            descriptorWrites[3].dstArrayElement = 0;
            descriptorWrites[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
            descriptorWrites[3].descriptorCount = 1;
            descriptorWrites[3].pImageInfo = &imageInfo;

            vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
        }
    }
void createDescriptorSetLayouts() {
        VkDescriptorSetLayoutBinding uboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding modelUboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding jointUboLayoutBinding{};
        uboLayoutBinding.binding = 0;
        uboLayoutBinding.descriptorCount = 1;
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.pImmutableSamplers = nullptr;
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

        VkDescriptorSetLayoutBinding samplerLayoutBinding{};
        samplerLayoutBinding.binding = 0;
        samplerLayoutBinding.descriptorCount = 1;
        samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        samplerLayoutBinding.pImmutableSamplers = nullptr;
        samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

        std::array<VkDescriptorSetLayoutBinding, 1> bindings1 = {uboLayoutBinding};
        VkDescriptorSetLayoutCreateInfo vpLayoutInfo{};
        vpLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        vpLayoutInfo.bindingCount = static_cast<uint32_t>(bindings1.size());
        vpLayoutInfo.pBindings = bindings1.data();

        if (vkCreateDescriptorSetLayout(device, &vpLayoutInfo, nullptr, &viewProjectionDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }

        std::array<VkDescriptorSetLayoutBinding, 1> bindings2 = {modelUboLayoutBinding};
        VkDescriptorSetLayoutCreateInfo modelLayoutInfo{};
        modelLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        modelLayoutInfo.bindingCount = static_cast<uint32_t>(bindings2.size());
        modelLayoutInfo.pBindings = bindings2.data();

        if (vkCreateDescriptorSetLayout(device, &modelLayoutInfo, nullptr, &modelDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }

        std::array<VkDescriptorSetLayoutBinding, 1> bindings3 = {jointUboLayoutBinding};
        VkDescriptorSetLayoutCreateInfo jointLayoutInfo{};
        jointLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        jointLayoutInfo.bindingCount = static_cast<uint32_t>(bindings3.size());
        jointLayoutInfo.pBindings = bindings3.data();

        if (vkCreateDescriptorSetLayout(device, &jointLayoutInfo, nullptr, &jointDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }

        std::array<VkDescriptorSetLayoutBinding, 1> bindings4 = {samplerLayoutBinding};
        VkDescriptorSetLayoutCreateInfo samplerLayoutInfo{};
        samplerLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        samplerLayoutInfo.bindingCount = static_cast<uint32_t>(bindings4.size());
        samplerLayoutInfo.pBindings = bindings4.data();

        if (vkCreateDescriptorSetLayout(device, &samplerLayoutInfo, nullptr, &samplerDescriptorSetLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create descriptor set layout!");
        }
    }
#version 450

layout(set = 0, binding = 0) uniform UniformBufferObject {
    mat4 view;
    mat4 proj;
} ubo;

layout(set = 1, binding = 0) uniform ModelUniformBufferObject {
    mat4 model;
} modelUbo;

layout(set = 2, binding = 0) uniform JointsUBO {
    mat4 bones[100];
} joints;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
    gl_Position = ubo.proj * ubo.view * modelUbo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}
#version 450

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

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(texSampler, fragTexCoord);
}

the descriptor pool is the same but I did update the pipeline creation code to create MAX_FRAMES_IN_FLIGHT descriptor set layouts (8 layouts in total because I have 4 descriptor sets and MAX_FRAMES_IN_FLIGHT is 2)

VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
        std::vector<VkDescriptorSetLayout> layouts;
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(viewProjectionDescriptorSetLayout);
        }
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(modelDescriptorSetLayout);
        }
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(jointDescriptorSetLayout);
        }
        for(unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
            layouts.push_back(samplerDescriptorSetLayout);
        }
        pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
        pipelineLayoutInfo.setLayoutCount = MAX_FRAMES_IN_FLIGHT*4;
        pipelineLayoutInfo.pSetLayouts = layouts.data();

        if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create pipeline layout!");
        }

It’s still not working :confused: here is the error message:

validation layer: Validation Error: [ VUID-VkGraphicsPipelineCreateInfo-layout-00756 ] Object 0: handle = 0x9fde6b0000000014, type = VK_OBJECT_TYPE_SHADER_MODULE; Object 1: handle = 0xdd3a8a0000000015, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0x45717876 | vkCreateGraphicsPipelines(): pCreateInfos[0] Set 3 Binding 0 in shader (VK_SHADER_STAGE_FRAGMENT_BIT) uses descriptor slot but descriptor not accessible from stage VK_SHADER_STAGE_FRAGMENT_BIT The Vulkan spec states: layout must be consistent with all shaders specified in pStages 
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x56c9bd0000000040, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[1] failed write update validation for VkDescriptorSet 0x56c9bd0000000040[] with error: VkDescriptorSet 0x56c9bd0000000040[] allocated with VkDescriptorSetLayout 0xd5b26f0000000010[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount 
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x89e60f0000000042, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[2] failed write update validation for VkDescriptorSet 0x89e60f0000000042[] with error: VkDescriptorSet 0x89e60f0000000042[] allocated with VkDescriptorSetLayout 0x980f360000000011[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount 
Segmentation fault (core dumped)

From your code, I’m getting the impression that you don’t really know what these things actually mean. You seem really confused about what a descriptor set layout is, what a pipeline layout is, and how those relate to whatever “MAX_FRAMES_IN_FLIGHT” is supposed to mean (here’s a hint: they are completely unrelated to that).

Overall, it really feels like you’re just trying stuff and seeing what gives you an error and what doesn’t.

Just to make this clear, a descriptor set layout or pipeline layout is like a class in C++. It’s a prototype, a definition of how an object will look. A class is not itself an object; it’s a thing you use to make objects. A single class can be used to make multiple objects of the same type, just with potentially different data in them.

So there is no reason why the number of sets in a pipeline layout should in any way be affected by the number of “frames in flight”. You may have multiple descriptor sets based on the number of “frames in flight”, just as you might have a number of objects that have different data in them. But they’re all the same layout/class.

I do need MAX_FRAMES_IN_FLIGHT descriptor sets because otherwise data from one frame would affect the rendering of previous frames in flight when I update the descriptor set. Rendering multiple frames in flight is an optimization because the cpu can keep doing work while the gpu is rendering previous frames.

But not layouts. Sets and set layouts are not the same thing. You can make many sets from a single layout, just like you can make many instances of classes from a single class.

Or do you make a new Vertex class every time you want to create a Vertex object?

But I need 4 different descriptor sets so I have to create 4 layouts: for the projection and view matrix set, the model matrix set, the joint set, and the image sampler set

For the VkDescriptorSetAllocateInfo struct I need to specify the descriptor set layouts that each descriptor set will use I have to pass a pointer to a descriptor set layout (in pSetLayouts) and the number of descriptor set layouts which has to match the descriptorSetCount.

What part of the code should I change? isn’t createDescriptorSetLayouts ok as it is because I need a descriptor set layout for each different set since I have 4 different sets I have 4 different descriptor set layouts:

VkDescriptorSetLayout viewProjectionDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
VkDescriptorSetLayout jointDescriptorSetLayout;
VkDescriptorSetLayout samplerDescriptorSetLayout;

But you’re not using 4 layouts; you’re using sixteen. Your layouts vector has 16 total copies of 4 sets.

Your shader only uses 4 sets. Your pipeline layout should reflect that.

Does the order of the layouts in the array I pass as a pointer in pSetLayouts matter?
I’ve changed my pipeline creation code:

VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
        std::vector<VkDescriptorSetLayout> layouts;
        layouts.push_back(viewProjectionDescriptorSetLayout);
        layouts.push_back(modelDescriptorSetLayout);
        layouts.push_back(jointDescriptorSetLayout);
        layouts.push_back(samplerDescriptorSetLayout);
        
        pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
        pipelineLayoutInfo.setLayoutCount = 4;
        pipelineLayoutInfo.pSetLayouts = layouts.data();

        if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
            throw std::runtime_error("failed to create pipeline layout!");
        }

I still get an error:

validation layer: Validation Error: [ VUID-VkGraphicsPipelineCreateInfo-layout-00756 ] Object 0: handle = 0xd175b40000000013, type = VK_OBJECT_TYPE_SHADER_MODULE; Object 1: handle = 0xdd3a8a0000000015, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0x45717876 | vkCreateGraphicsPipelines(): pCreateInfos[0] Set 1 Binding 0 in shader (VK_SHADER_STAGE_VERTEX_BIT) uses descriptor slot but descriptor not accessible from stage VK_SHADER_STAGE_VERTEX_BIT The Vulkan spec states: layout must be consistent with all shaders specified in pStages
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x56c9bd0000000040, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[1] failed write update validation for VkDescriptorSet 0x56c9bd0000000040[] with error: VkDescriptorSet 0x56c9bd0000000040[] allocated with VkDescriptorSetLayout 0xd5b26f0000000010[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount
validation layer: Validation Error: [ VUID-VkWriteDescriptorSet-dstBinding-00316 ] Object 0: handle = 0x89e60f0000000042, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xcd9212c1 | vkUpdateDescriptorSets() pDescriptorWrites[2] failed write update validation for VkDescriptorSet 0x89e60f0000000042[] with error: VkDescriptorSet 0x89e60f0000000042[] allocated with VkDescriptorSetLayout 0x980f360000000011[] cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount
Segmentation fault (core dumped)

How else would Vulkan know what “set 0” is? It’s an index into that very array.

Are there any more mistakes in the code that I’ve shared? it’s still not working.