描述符集

有了描述符池,现在我们可以开始创建描述符集对象了。添加一个叫做createDescriptorSets的函数来完成描述符集对象的创建:

void initVulkan() {
        ...
    createDescriptorPool();
    createDescriptorSets();
        ...
}

        ...

void createDescriptorSets() {

}

描述符集的创建需要我们填写VkDescriptorSetAllocateInfo结构体。我们需要指定分配描述符集对象的描述符池,需要分配的描述符集数量,以及它们使用的描述符布局:

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

在这里,我们为每一个交换链图像使用相同的描述符布局创建对应的描述符集。但由于描述符布局对象个数要匹配描述符集对象个数,所以,我们还是需要使用多个相同的描述符布局对象。

添加类成员变量来存储使用vkAllocateDescriptorSets函数创建的描述符集对象:

VkDescriptorPool descriptorPool;
std::vector<VkDescriptorSet> descriptorSets;

        ...

descriptorSets.resize(swapChainImages.size());
if (vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets[0]) != VK_SUCCESS) {
    throw std::runtime_error("failed to allocate descriptor sets!");
}

描述符集对象会在描述符池对象清除时自动被清除,所以不需要我们自己显式地清除描述符对象。vkAllocateDescriptorSets函数分配地描述符集对象,每一个都带有一个uniform缓冲描述符。

描述符集对象创建后,还需要进行一定地配置。我们使用循环来遍历描述符集对象,对它进行配置:

for (size_t i = 0; i < swapChainImages.size(); i++) {

}

我们通过VkDescriptorBufferInfo结构体来配置描述符引用的缓冲对象。VkDescriptorBufferInfo结构体可以指定缓冲对象和可以访问的数据范围:

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

如果读者需要使用整个缓冲,可以将range成员变量的值设置为VK_WHOLE_SIZE。更新描述符的配置可以使用vkUpdateDescriptorSets函数进行,它以VkWriteDescriptorSet结构体数组作为参数:

VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSets[i];
descriptorWrite.dstBinding = 0;
descriptorWrite.dstArrayElement = 0;

dstSet和dstBinding成员变量用于指定要更新的描述符集对象以及缓冲绑定。在这里,我们将uniform缓冲绑定到索引0。需要注意描述符可以是数组,所以我们还需要指定数组的第一个元素的索引,在这里,我们没有使用数组作为描述符,将索引指定为0即可。

descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = 1;

我们需要再次指定描述符的类型。可以通过设置dstArrayElement和descriptorCount成员变量一次更新多个描述符。

descriptorWrite.pBufferInfo = &bufferInfo;
descriptorWrite.pImageInfo = nullptr; // Optional
descriptorWrite.pTexelBufferView = nullptr; // Optional

pBufferInfo成员变量用于指定描述符引用的缓冲数据。pImageInfo成员变量用于指定描述符引用的图像数据。pTexelBufferView成员变量用于指定描述符引用的缓冲视图。这里,我们的描述符使用来访问缓冲的,所以我们只使用pBufferInfo成员变量。

vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);

vkUpdateDescriptorSets函数可以接受两个数组作为参数:VkWriteDescriptorSet结构体数组和VkCopyDescriptorSet结构体数组。后者被用来复制描述符对象。