下面的示例是在Vulkan计算管线中创建描述符池和描述符集,并在计算管线中调用它们。首先创建两个描述符集合:
1. 描述符集0:包含两个绑定点,每个绑定点有3个描述符。
2. 描述符集1:包含一个绑定点,有4个描述符。
我们将在同一个计算管线中使用这些描述符。
1. 定义描述符布局
首先定义描述符布局,描述符布局将设定每个绑定点及其对应的描述符类型和数量。这里假设Set 0, Binding 0有三个UNIFORM_BUFFER描述符,Set 0, Binding 1有三个STORAGE_BUFFER描述符,Set 1, Binding 0有四个STORAGE_BUFFER描述符。代码如下:
VkDescriptorSetLayoutBinding bindings0[2];
// Set 0, Binding 0: uniform buffer with 3 descriptors
bindings0[0].binding = 0;
bindings0[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings0[0].descriptorCount = 3;
bindings0[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings0[0].pImmutableSamplers = nullptr;
// Set 0, Binding 1: storage buffer with 3 descriptors
bindings0[1].binding = 1;
bindings0[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings0[1].descriptorCount = 3;
bindings0[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings0[1].pImmutableSamplers = nullptr;
VkDescriptorSetLayoutCreateInfo layoutInfo0 = {};
layoutInfo0.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo0.bindingCount = 2;
layoutInfo0.pBindings = bindings0;
VkDescriptorSetLayout setLayout0;
if (vkCreateDescriptorSetLayout(device, &layoutInfo0, nullptr, &setLayout0) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor set layout 0!");
}
VkDescriptorSetLayoutBinding bindings1;
// Set 1, Binding 0: storage buffer with 4 descriptors
bindings1.binding = 0;
bindings1.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings1.descriptorCount = 4;
bindings1.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings1.pImmutableSamplers = nullptr;
VkDescriptorSetLayoutCreateInfo layoutInfo1 = {};
layoutInfo1.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo1.bindingCount = 1;
layoutInfo1.pBindings = &bindings1;
VkDescriptorSetLayout setLayout1;
if (vkCreateDescriptorSetLayout(device, &layoutInfo1, nullptr, &setLayout1) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor set layout 1!");
}
2. 创建描述符池
接下来创建描述符池。描述符池管理描述符集合的内存分配。
VkDescriptorPoolSize poolSizes[] = {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 7 }
};
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 2;
poolInfo.pPoolSizes = poolSizes;
poolInfo.maxSets = 2;
VkDescriptorPool descriptorPool;
if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!");
}
3. 分配描述符集
接下来,根据布局从描述符池中分配描述符集。
VkDescriptorSetLayout layouts[] = { setLayout0, setLayout1 };
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = 2;
allocInfo.pSetLayouts = layouts;
VkDescriptorSet descriptorSets[2];
if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!");
}
4. 更新描述符集
将实际的buffer绑定到描述符集合中。
VkDescriptorBufferInfo bufferInfos0[3];
bufferInfos0[0] = { buffer0, 0, VK_WHOLE_SIZE };
bufferInfos0[1] = { buffer1, 0, VK_WHOLE_SIZE };
bufferInfos0[2] = { buffer2, 0, VK_WHOLE_SIZE };
VkDescriptorBufferInfo bufferInfos1_0[3];
bufferInfos1_0[0] = { buffer3, 0, VK_WHOLE_SIZE };
bufferInfos1_0[1] = { buffer4, 0, VK_WHOLE_SIZE };
bufferInfos1_0[2] = { buffer5, 0, VK_WHOLE_SIZE };
VkDescriptorBufferInfo bufferInfos2[4];
bufferInfos2[0] = { buffer6, 0, VK_WHOLE_SIZE };
bufferInfos2[1] = { buffer7, 0, VK_WHOLE_SIZE };
bufferInfos2[2] = { buffer8, 0, VK_WHOLE_SIZE };
bufferInfos2[3] = { buffer9, 0, VK_WHOLE_SIZE };
VkWriteDescriptorSet descriptorWrites[3];
// Set 0, Binding 0: uniform buffers
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSets[0];
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[0].descriptorCount = 3;
descriptorWrites[0].pBufferInfo = bufferInfos0;
// Set 0, Binding 1: storage buffers
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = descriptorSets[0];
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptorWrites[1].descriptorCount = 3;
descriptorWrites[1].pBufferInfo = bufferInfos1_0;
// Set 1, Binding 0: storage buffers
descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[2].dstSet = descriptorSets[1];
descriptorWrites[2].dstBinding = 0;
descriptorWrites[2].dstArrayElement = 0;
descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptorWrites[2].descriptorCount = 4;
descriptorWrites[2].pBufferInfo = bufferInfos2;
vkUpdateDescriptorSets(device, 3, descriptorWrites, 0, nullptr);
5. 创建计算管线
接下来,我们将创建计算管线,并绑定描述符集。
VkPipelineLayout pipelineLayout;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 2;
VkDescriptorSetLayout setLayouts[] = { setLayout0, setLayout1 };
pipelineLayoutInfo.pSetLayouts = setLayouts;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
VkPipeline computePipeline;
VkComputePipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipelineInfo.stage = computeShaderStageInfo; // Assume computeShaderStageInfo is already defined
pipelineInfo.layout = pipelineLayout;
if (vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) {
throw std::runtime_error("failed to create compute pipeline!");
}
6. 绑定描述符集并调度计算管线
最后,我们将绑定描述符集,并调度计算管线。
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 2, descriptorSets, 0, nullptr);
vkCmdDispatch(commandBuffer, 1, 1, 1);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
到这里就完成Vulkan计算管线创建并配置复杂的描述符池和描述符集,并在计算管线中调用。