当前位置: 首页 > news >正文

Vulkan 学习(14)---- 描述符集

目录

      • Vulkan DescriptorSet
        • 描述符布局和管线布局
        • 创建和使用描述符集
        • 参考代码

Vulkan DescriptorSet

Vulkan 中,描述符是一种在着色器中访问资源(比如缓冲区,图像,采样器等)的机制或者协议

每个描述符对应一个资源,代表 GPU 内存中的资源,比如 Uniform Bufferstorage Buffer, TextureSampler

Vulkan 描述符集(VkDescriptorSet)表示着色器可以与之交互的资源的集合,着色器是通过描述符读取和解析资源中的数据,着色器中的绑定点和相应的描述符集中的绑定点必须一一对应

Vulkan 描述符集是不能被直接创建的,首先需要从一个特定的缓冲池中被分配得到。这个池子叫做描述符池(VkDescriptorPool),类似于内存池的概念

vulkanDescriptorSet
VkDescriptorPool 负责分配新的 Descriptor 对象,换句话说,它相当于一组描述符的集合,新的描述符就是从这些描述符中分配得到的

VkDescriptorPool 对于内存分配效率较低的场合是非常有用的,它可以直接分配出多组描述符而不需要调用全局同步操作

在创建 DescriptorSet 之前,需要定义 DescriptorSet的布局(VkDescriporSetLayout),布局指定了描述符的类型,数量,绑定点等信息

描述符布局和管线布局

VkDescriporSetLayoutVkPipelineLayout 的关系是什么:

管线布局是对着色器资源绑定的全局描述,代表了图形管线可以访问的所有资源的集合

创建 Vulkan 渲染管线的时候需要设置管线布局,它描述了渲染过程中着色器如何访问资源,包括描述符集和推送常量等

管线布局可以包括一个或者多个描述符布局和推送常量描述(推送常量是可以更新着色器中的常量数据),下面是创建管线布局的结构体:

typedef struct VkPipelineLayoutCreateInfo {
    VkStructureType                 sType;
    const void*                     pNext;
    VkPipelineLayoutCreateFlags     flags;
    uint32_t                        setLayoutCount;
    const VkDescriptorSetLayout*    pSetLayouts;
    uint32_t                        pushConstantRangeCount;
    const VkPushConstantRange*      pPushConstantRanges;
} VkPipelineLayoutCreateInfo;

无论是管线布局还是描述符布局,本质都是对资源的一种描述,其本身并不占用资源

创建和使用描述符集

创建和使用描述符集

  1. 定义描述符集布局
  • 使用 VkDescriptorSetLayoutBinding 结构体定义每个描述符的类型、数量和绑定点
  • 调用 vkCreateDescriptorSetLayout 创建描述符集布局
  1. 创建描述符池
  • 使用 VkDescriptorPoolSize 指定描述符池的每种描述符类型的数量
  • 调用 vkCreateDescriptorPool 创建描述符池
  1. 分配描述符集
  • 调用 vkAllocateDescriptorSets 从描述符池中分配描述符集
  1. 更新描述符集
  • 使用 VkWriteDescriptorSet 结构体更新描述符集中的描述符,绑定实际的资源(比如缓冲区、纹理等)
  1. 绑定描述符集
  • 在渲染过程中,调用 vkCmdBindDescriptorSets 将描述符集绑定到管线,着色器就可以访问描述符集中指定的资源
参考代码
  1. 定义描述符集布局
    创建一个含有三个绑定点的 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[ ];
};
  1. 创建描述符池DescriptorPool

根据 VkDescriptorSet 的类型,分别创建一个分配 UniformBufferstorageBufferdescriptorPool,
注意要定义最大分配的 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!");
    }
}
  1. 分配描述符集 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!");
    }
  1. 更新描述符集
    通过 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);
    }
  1. 绑定描述符集
    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!");
    }
    }

相关文章:

  • 社群效应与开源AI智能名片2+1链动模式S2B2C商城小程序在流量变现中的应用探索
  • 什么是3D视觉无序抓取?
  • 153~173笔记
  • 基于SpringBoot的鲜花商城
  • 通用评估系统(五)- 前端部分总体说明
  • qt QOpenGLTexture详解
  • 算法【贪心经典题目专题4】
  • 用AI写SQL2——递归查询WITH RECURSIVE
  • 科技云报到:科技普惠潮流渐起,“开源”将带我们走向何方?
  • 北京海百川科技有限公司:以智能体技术助力特殊教育行业
  • Python中如何进行数据库连接?
  • [题解]2024CCPC重庆站-小 C 的神秘图形
  • 智能马达保护器:为工业电机安全运行保驾护航
  • HepG2细胞复苏实验以及六孔板种植细胞实验
  • JavaEE基础 Tomcat与Http (下)
  • 【Prometheus】prometheus结合domain_exporter实现域名监控
  • 矩阵碰一碰发视频源码源头搭建,支持OEM
  • PLC的五个学习步骤
  • Linux学习笔记之虚拟地址空间
  • Spring 是如何解决循环依赖问题的?
  • 智能终端出海服务创新联合体成立
  • 神十九飞船已撤离空间站,计划于今日中午返回东风着陆场
  • 民生访谈|规范放生活动、提升供水品质……上海将有这些举措
  • “一对一讨论诸多事宜”,泽连斯基披露此次特泽会更多细节
  • 俄罗斯称已收复库尔斯克州
  • 迟来的忍者与武士:从《刺客信条:影》论多元话语的争议