Vulkan学习(14)----描述符集
- 创业
- 2025-09-06 05:09:02

目录 Vulkan DescriptorSet描述符布局和管线布局创建和使用描述符集参考代码 Vulkan DescriptorSet
Vulkan 中,描述符是一种在着色器中访问资源(比如缓冲区,图像,采样器等)的机制或者协议
每个描述符对应一个资源,代表 GPU 内存中的资源,比如 Uniform Buffer, storage Buffer, Texture,Sampler等
Vulkan 描述符集(VkDescriptorSet)表示着色器可以与之交互的资源的集合,着色器是通过描述符读取和解析资源中的数据,着色器中的绑定点和相应的描述符集中的绑定点必须一一对应
Vulkan 描述符集是不能被直接创建的,首先需要从一个特定的缓冲池中被分配得到。这个池子叫做描述符池(VkDescriptorPool),类似于内存池的概念
VkDescriptorPool 负责分配新的 Descriptor 对象,换句话说,它相当于一组描述符的集合,新的描述符就是从这些描述符中分配得到的
VkDescriptorPool 对于内存分配效率较低的场合是非常有用的,它可以直接分配出多组描述符而不需要调用全局同步操作
在创建 DescriptorSet 之前,需要定义 DescriptorSet的布局(VkDescriporSetLayout),布局指定了描述符的类型,数量,绑定点等信息
描述符布局和管线布局VkDescriporSetLayout 和 VkPipelineLayout 的关系是什么:
管线布局是对着色器资源绑定的全局描述,代表了图形管线可以访问的所有资源的集合
创建 Vulkan 渲染管线的时候需要设置管线布局,它描述了渲染过程中着色器如何访问资源,包括描述符集和推送常量等
管线布局可以包括一个或者多个描述符布局和推送常量描述(推送常量是可以更新着色器中的常量数据),下面是创建管线布局的结构体:
typedef struct VkPipelineLayoutCreateInfo { VkStructureType sType; const void* pNext; VkPipelineLayoutCreateFlags flags; uint32_t setLayoutCount; const VkDescriptorSetLayout* pSetLayouts; uint32_t pushConstantRangeCount; const VkPushConstantRange* pPushConstantRanges; } VkPipelineLayoutCreateInfo;无论是管线布局还是描述符布局,本质都是对资源的一种描述,其本身并不占用资源
创建和使用描述符集创建和使用描述符集
定义描述符集布局 使用 VkDescriptorSetLayoutBinding 结构体定义每个描述符的类型、数量和绑定点调用 vkCreateDescriptorSetLayout 创建描述符集布局 创建描述符池 使用 VkDescriptorPoolSize 指定描述符池的每种描述符类型的数量调用 vkCreateDescriptorPool 创建描述符池 分配描述符集 调用 vkAllocateDescriptorSets 从描述符池中分配描述符集 更新描述符集 使用 VkWriteDescriptorSet 结构体更新描述符集中的描述符,绑定实际的资源(比如缓冲区、纹理等) 绑定描述符集 在渲染过程中,调用 vkCmdBindDescriptorSets 将描述符集绑定到管线,着色器就可以访问描述符集中指定的资源 参考代码 定义描述符集布局 创建一个含有三个绑定点的 VkDescriptorSetLayout,相应的要创建三个 VkDescriptorSetLayoutBinding void createComputeDescriptorSetLayout() { std::array<VkDescriptorSetLayoutBinding, 3> layoutBindings{}; layoutBindings[0].binding = 0; // 绑定点 0 layoutBindings[0].descriptorCount = 1; layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; layoutBindings[0].pImmutableSamplers = nullptr; layoutBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; layoutBindings[1].binding = 1; // 绑定点 1 layoutBindings[1].descriptorCount = 1; layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; layoutBindings[1].pImmutableSamplers = nullptr; layoutBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; layoutBindings[2].binding = 2;// 绑定点 2 layoutBindings[2].descriptorCount = 1; layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; layoutBindings[2].pImmutableSamplers = nullptr; layoutBindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; VkDescriptorSetLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.bindingCount = 3; layoutInfo.pBindings = layoutBindings.data(); if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create compute descriptor set layout!"); } }对应的 GLSL 代码为:
layout (binding = 0) uniform ParameterUBO { float deltaTime; } ubo; layout(std140, binding = 1) readonly buffer ParticleSSBOIn { Particle particlesIn[ ]; }; layout(std140, binding = 2) buffer ParticleSSBOOut { Particle particlesOut[ ]; }; 创建描述符池DescriptorPool根据 VkDescriptorSet 的类型,分别创建一个分配 UniformBuffer 和 storageBuffer 的 descriptorPool, 注意要定义最大分配的 descriptorCount
void createDescriptorPool() { std::array<VkDescriptorPoolSize, 2> 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_STORAGE_BUFFER; poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT) * 2; VkDescriptorPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = 2; 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!"); } } 分配描述符集 VkDescriptorSet std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, computeDescriptorSetLayout); 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(); computeDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT); if (vkAllocateDescriptorSets(device, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) { throw std::runtime_error("failed to allocate descriptor sets!"); } 更新描述符集 通过 VkWriteDescriptorSet 更新 DescriptorSet for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VkDescriptorBufferInfo uniformBufferInfo{}; uniformBufferInfo.buffer = uniformBuffers[i]; uniformBufferInfo.offset = 0; uniformBufferInfo.range = sizeof(UniformBufferObject); std::array<VkWriteDescriptorSet, 3> descriptorWrites{}; descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites[0].dstSet = computeDescriptorSets[i]; descriptorWrites[0].dstBinding = 0; descriptorWrites[0].dstArrayElement = 0; descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorWrites[0].descriptorCount = 1; descriptorWrites[0].pBufferInfo = &uniformBufferInfo; VkDescriptorBufferInfo storageBufferInfoLastFrame{}; storageBufferInfoLastFrame.buffer = shaderStorageBuffers[(i - 1) % MAX_FRAMES_IN_FLIGHT]; storageBufferInfoLastFrame.offset = 0; storageBufferInfoLastFrame.range = sizeof(Particle) * PARTICLE_COUNT; descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites[1].dstSet = computeDescriptorSets[i]; descriptorWrites[1].dstBinding = 1; descriptorWrites[1].dstArrayElement = 0; descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; descriptorWrites[1].descriptorCount = 1; descriptorWrites[1].pBufferInfo = &storageBufferInfoLastFrame; VkDescriptorBufferInfo storageBufferInfoCurrentFrame{}; storageBufferInfoCurrentFrame.buffer = shaderStorageBuffers[i]; storageBufferInfoCurrentFrame.offset = 0; storageBufferInfoCurrentFrame.range = sizeof(Particle) * PARTICLE_COUNT; descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites[2].dstSet = computeDescriptorSets[i]; descriptorWrites[2].dstBinding = 2; descriptorWrites[2].dstArrayElement = 0; descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; descriptorWrites[2].descriptorCount = 1; descriptorWrites[2].pBufferInfo = &storageBufferInfoCurrentFrame; vkUpdateDescriptorSets(device, 3, descriptorWrites.data(), 0, nullptr); } 绑定描述符集 void recordComputeCommandBuffer(VkCommandBuffer commandBuffer) { VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { throw std::runtime_error("failed to begin recording compute command buffer!"); } vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &computeDescriptorSets[currentFrame], 0, nullptr); vkCmdDispatch(commandBuffer, PARTICLE_COUNT / 256, 1, 1); if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { throw std::runtime_error("failed to record compute command buffer!"); } }Vulkan学习(14)----描述符集由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Vulkan学习(14)----描述符集”
上一篇
dockerpush镜像到阿里云