您的位置:首页 > 文旅 > 美景 > Vulkan描述符、描述符Pool、Layout概念

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

2024/10/7 4:32:17 来源:https://blog.csdn.net/zxbcollegestudent/article/details/141969758  浏览:    关键词: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_SIZEbufferInfo.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 SAMPLERstd::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 : enablelayout(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博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com