Vulkan描述符、描述符Pool、Layout概念

  1、DescriptorSetLayout为了组织和管理着色器资源(如缓冲区、纹理、采样器等),多个相同类型的Descriptor放在一个Layout中以优化GPU对资源的访问

  //DescriptorSetLayout定义了哪些描述符Descriptor类型(Buffers、Textures、Samplers)可以包含在其中

VkDescriptorSetLayoutBinding layoutBinding = {
        0, // binding
        VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,//DescriptorSetLayout布局中的描述符类型,表明只有UNIFORM Buffer才能包含在下面创建的DescriptorSetLayout中
        1,//descriptorCount
        VK_SHADER_STAGE_VERTEX_BIT,//stageFlags
        nullptr
    };
 
    VkDescriptorSetLayoutCreateInfo descLayoutInfo{};
    descLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    descLayoutInfo.bindingCount = 1;
    descLayoutInfo.pBindings = &layoutBinding;

    err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
    if (err != VK_SUCCESS)
        qFatal("Failed to create descriptor set layout: %d", err);

    //提供着色器使用的每一个描述符绑定信息
    void createDescriptorSetLayout(){
        VkDescriptorSetLayoutBinding uboLayoutBinding = {};
        uboLayoutBinding.binding = 0;//.vert或.frag文件中layout (set = 0, binding = 0)...
        uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        uboLayoutBinding.descriptorCount = 1;
        //在哪一个着色器阶段被使用,这里只在顶点着色器被使用
        uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;//说明写在.vert文件中并使用
        uboLayoutBinding.pImmutableSamplers = nullptr ;

        //用于组合图像采样器描述符的 VkDescriptorSetLayoutBinding 结构体信息
        VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
        samplerLayoutBinding.binding = 1;//.vert或.frag文件中layout (set = 0, binding = 1)...
        samplerLayoutBinding.descriptorCount = 1;
        samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        samplerLayoutBinding.pImmutableSamplers = nullptr;
        /**
        stageFlags 成员变量指明在片段着色器中使用组合图像采样器描述符。
        在顶点着色器也可以进行纹理采样,一个常见的用途是在顶点着色
        器中使用高度图纹理来对顶点进行变形
          */
        samplerLayoutBinding.stageFlags =  VK_SHADER_STAGE_FRAGMENT_BIT;//说明写在.frag文件中并使用


        std::array<VkDescriptorSetLayoutBinding,2> bindings = 
        {
            uboLayoutBinding,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");
        }
    }

 2、DescriptorPool

 //创建 Descriptor Pool (描述符池)是为了管理 DescriptorSet 的分配释放
    VkDescriptorPoolSize descPoolSizes{};  //也可以声明为vector<VkDescriptorPoolSize>以指定多种 Descriptor 类型
    descPoolSizes.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;//描述符类型
    descPoolSizes.descriptorCount = static_cast<uint32_t>(concurrentFrameCount);//UNIFORM BUFFER个数
    
    VkDescriptorPoolCreateInfo poolInfo{};
    poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    poolInfo.pNext = nullptr;
    poolInfo.maxSets = static_cast<uint32_t>(concurrentFrameCount);
    poolInfo.poolSizeCount = 1;
    poolInfo.pPoolSizes = &descPoolSizes;
    if (m_devFuncs->vkCreateDescriptorPool(dev, &poolInfo, nullptr, &m_descPool) != VK_SUCCESS)
    {
        throw std::runtime_error("echec de la creation de la pool de descripteurs!");
    }

   //描述符池的创建
    void createDescriptorPool(){
        std::array<VkDescriptorPoolSize,2> poolSizes = {};
        //添加UNIFORM BUFFER描述符
        poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        poolSizes[0].descriptorCount = static_cast<uint32_t>(swapChainImages.size());

        //添加图像采样器描述符
        poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        poolSizes[1].descriptorCount = static_cast<uint32_t>(swapChainImages.size());

        //指定描述符池的大小,我们会在每一帧分配一个描述符
        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>(swapChainImages.size());;
        if(vkCreateDescriptorPool(device,&poolInfo,nullptr, &descriptorPool) != VK_SUCCESS){
            throw std::runtime_error("failed to create descriptor pool!");
        }
    }

用于分配描述符DescriptorSet的DescriptorPool和用于组织和管理描述符Descriptor的DescriptorSetLayout都已经准备好了,接下来就是创建DescriptorSet了,只要完成了这一步,后面就可以把创建的Descriptor写入DescriptorSet中了

 3、DescriptorSet

     std::vector<VkDescriptorSetLayout> layouts(concurrentFrameCount, m_descSetLayout);
    VkDescriptorSetAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    allocInfo.pNext = nullptr;
    allocInfo.descriptorPool = m_descPool;
    allocInfo.descriptorSetCount = static_cast<uint32_t>(concurrentFrameCount);
    allocInfo.pSetLayouts = layouts.data();
    //Descriptor Set(描述符集)是一组Descriptor的集合
    m_descriptorSets.resize(concurrentFrameCount);
    if (m_devFuncs->vkAllocateDescriptorSets(dev, &allocInfo, m_descriptorSets.data()) != VK_SUCCESS)
    {
        throw std::runtime_error("echec de l'allocation d'un set de descripteurs!");
    }

 DescriptorSet现在已经在DescriptorSetLayout中了。

  //每种类型的 Descriptor,如 Uniform Buffers、Sampled Images、Storage Buffers、Textures 等,每个Descriptor包含了指向实际资源的引用。

 创建Uniform Buffers描述符:

    VkDeviceSize bufferSize = sizeof(UniformBufferObject);
    m_uniformBuffers.resize(concurrentFrameCount); // concurrentFrameCount correspond à swapchainimages.size()
    m_uniformBuffersMemory.resize(concurrentFrameCount);
    for (size_t i = 0; i < concurrentFrameCount; i++)
    {
        createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
            m_uniformBuffers[i], 
            m_uniformBuffersMemory[i]);
    }

 4、接下来把描述符 m_uniformBuffers 写入DescriptorSet中

void Renderer::flushTexToDescriptorSet(VkDevice dev, int concurrentFrameCount)
{
    for (int i = 0; i < concurrentFrameCount; ++i) {

        VkDescriptorBufferInfo uniformBufferInfo{};
        uniformBufferInfo.buffer = m_uniformBuffers[i];//创建的VkBuffer描述符类型
        uniformBufferInfo.offset = 0;
        uniformBufferInfo.range = sizeof(UniformBufferObject);

        VkWriteDescriptorSet descriptorWrite{};
        descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
        descriptorWrite.pNext = NULL;
        descriptorWrite.dstSet = m_descriptorSets[i];//写入到索引i的DescriptorSet中
        descriptorWrite.dstBinding = 0;
        descriptorWrite.dstArrayElement = 0;
        descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
        descriptorWrite.descriptorCount = 1;
        descriptorWrite.pBufferInfo = &uniformBufferInfo;
        descriptorWrite.pImageInfo = nullptr; // Optionnel
        descriptorWrite.pTexelBufferView = nullptr; // Optionnel

        m_devFuncs->vkUpdateDescriptorSets(dev, 1, &descriptorWrite, 0, nullptr);//写入
    }
}

 //创建描述符集对象
    void createDescriptorSets(){
        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();

        /**
        在这里,我们为每一个交换链图像使用相同的描述符布局创建对应的描述符集。
        但由于描述符布局对象个数要匹配描述符集对象个数,所以,
        我们还是需要使用多个相同的描述符布局对象。
          */
        descriptorSets.resize(swapChainImages.size());
        //分配地描述符集对象,每一个都带有一个 uniform 缓冲描述符
        if(vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS){
            throw std::runtime_error("failed to allocate descriptor sets!");
        }
        //配置描述符集对象
        for(size_t i =0;i<swapChainImages.size();i++){

            //1、准备UNIFORM BUFFER描述符信息
            VkDescriptorBufferInfo bufferInfo = {};    
            bufferInfo.buffer = uniformBuffers[i];
            bufferInfo.offset = 0;
            //可以访问的数据范围,需要使用整个缓冲,可以将range成员变量的值设置为 VK_WHOLE_SIZE
            bufferInfo.range = sizeof(UniformBufferObject);

            //2、准备IMAGE SAMPLER描述符信息
            VkDescriptorImageInfo imageInfo = {};
            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
            imageInfo.imageView =  textureImageView;
            imageInfo.sampler = textureSampler;

            //数组元素2个,一个为UNIFORM Buffer,一个为IMAGE SAMPLER
            std::array<VkWriteDescriptorSet , 2> descriptorWrites = {};


            //更新描述符的配置
            descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            //指定要更新的描述符集对象
            descriptorWrites[0].dstSet = descriptorSets[i];
            /**
            在这里,我们将 uniform 缓冲绑定到索引 0。需要注意描述符可以是数组,
            所以我们还需要指定数组的第一个元素的索引,在这里,我们
            没有使用数组作为描述符,将索引指定为 0 即可
              */
            //缓冲绑定
            descriptorWrites[0].dstBinding = 0;//.vert或.frag文件中layout (binding = 0)...
            descriptorWrites[0].dstArrayElement = 0;
            //指定描述符的类型
            descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            descriptorWrites[0].descriptorCount = 1;//指定描述符的数量
            //指定描述符引用的缓冲数据
            descriptorWrites[0].pBufferInfo = &bufferInfo;
            //指定描述符引用的图像数据
            descriptorWrites[0].pImageInfo = nullptr;
            //指定描述符引用的缓冲视图
            descriptorWrites[0].pTexelBufferView = nullptr;

            //更新图像和采样器到描述符配置
            descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
            descriptorWrites[1].dstSet = descriptorSets[i];
            descriptorWrites[1].dstBinding = 1;//.vert或.frag文件中layout (binding = 1)...
            descriptorWrites[1].dstArrayElement = 0;
            descriptorWrites[1].descriptorType=VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
            descriptorWrites[1].descriptorCount = 1;//指定描述符的数量
            descriptorWrites[1].pImageInfo = &imageInfo;
            /**
             * @brief vkUpdateDescriptorSets
             * 函数可以接受两个数组作为参数:
             * VkWriteDescriptorSet 结构体数组和 VkCopyDescriptorSet 结构体数组。
             * 后者被用来复制描述符对象
             */
            vkUpdateDescriptorSets(device,static_cast<uint32_t>(descriptorWrites.size()),
                                   descriptorWrites.data(),0,nullptr);
            //更新描述符需要使用图像资源信息。至此,我们就可以在着色器中使用描述符了
        }
    }

 顶点着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(binding = 0) uniform UniformBufferObject {
    mat4 model;
    mat4 view;
    mat4 proj;
} ubo;

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

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

out gl_PerVertex{
    vec4 gl_Position;
};

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

片段着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 1) 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);
}

5、渲染管线VkPipeline ,定义了渲染各个阶段配置,如下图整个渲染大致流程。

    pipelineInfo.layout = m_pipelineLayout;
    pipelineInfo.renderPass = m_window->defaultRenderPass();

    err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
    if (err != VK_SUCCESS)
        qFatal("Failed to create graphics pipeline: %d", err);

所有工作准备好了,接下来Start frame render

    VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();

//开始render
    m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
.................

//本次渲染使用的管线配置

    m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
................

//本次渲染使用的描述符资源DescriptorSet

    m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
        m_pipelineLayout, 
        0, 
        1, 
        &m_descriptorSets[currFrameIdx], 
        0, 
        nullptr);

............

//录制命令

m_devFuncs->vkCmdDrawIndexed(cb, indices.size(), 1, 0, 0, 0);

//结束render

m_devFuncs->vkCmdEndRenderPass(cmdBuf);

 Vulkan的DescriptorSet理解_vkallocatedescriptorsets-CSDN博客

Vulkan学习--20.组合图像采样器--实现矩形纹理贴图_vulkan sampler1d-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值