Vulkan Tutorial 24 Descriptor pool and sets
操作系统:Windows8.1
显卡:Nivida GTX965M
开发工具:Visual Studio 2017
Introduction
描述符布局描述了前一章节讨论过的可以绑定的描述符的类型。在本章节,我们创建描述符集,它将实际指定一个VkBuffer来绑定到一个uniform buffer描述符。
Descriptor pool
描述符集合不能直接创建,它们必须像命令缓冲区一样,从对象池中分配使用。对于描述符集合相当于调用描述符对象池。我们将写一个新的函数createDescriptorPool来配置。
void initVulkan() {
...
createUniformBuffer();
createDescriptorPool();
...
}
...
void createDescriptorPool() {
}
我们首先需要明确我们使用的描述符集合包含的描述符类型与数量,这里使用VkDescriptorPoolSize结构体。
VkDescriptorPoolSize poolSize = {};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = ;
现在我们只有一个uniform buffer类型的单描述符。对象池大小将被VkDescriptorPoolCreateInfo结构体引用:
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = ;
poolInfo.pPoolSizes = &poolSize;
我们也需要指定最大的描述符集合的分配数量:
poolInfo.maxSets = ;
该结构体与命令对象池类似,有一些可选项用于决定每个描述符集合是否可以独立管理生命周期:VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT。创建完毕后我们不会进一步使用它,所以我们不需要该flag。在这里设置flags默认值为0。
VkDescriptorPool descriptorPool;
...
if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!");
}
添加新的类成员对象保存描述符对象池的句柄,通过调用vkCreateDescriptorPool创建它。与其他绘制资源一样,描述符对象池应该仅在程序退出的时候销毁:
void cleanup() {
cleanupSwapChain();
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
...
}
Descriptor set
为了从对象池中分配描述符集合,我们需要添加一个createDescriptorSet函数:
void initVulkan() {
...
createDescriptorPool();
createDescriptorSet();
...
}
...
void createDescriptorSet() {
}
描述符集合通过VkDescriptorSetAllocateInfo结构体描述具体的分配。需要指定用于分配的描述符对象池,分配的描述符集合数量,以及基于此的描述符布局:
VkDescriptorSetLayout layouts[] = {descriptorSetLayout};
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = ;
allocInfo.pSetLayouts = layouts;
添加类成员存储描述符集合的句柄,并使用vkAllocateDescriptorSets分配:
VkDescriptorPool descriptorPool;
VkDescriptorSet descriptorSet; ... if (vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor set!");
}
我们不需要明确清理描述符集合,因为它们会在描述符对象池销毁的时候自动清理。调用vkAllocateDescriptorSets会分配一个具有uniform buffer描述符的描述符集合。
描述符集合已经分配了,但是内部的描述符需要配置。描述符需要引用缓冲区,就像uniform buffer描述符,使用VkDescriptorBufferInfo结构体进行配置。该结构体指定缓冲区和描述符内部包含的数据的区域:
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = uniformBuffer;
bufferInfo.offset = ;
bufferInfo.range = sizeof(UniformBufferObject);
描述符的配置更新使用vkUpdateDescriptorSets函数,它需要VkWriteDescriptorSet结构体的数组作为参数。
VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet;
descriptorWrite.dstBinding = ;
descriptorWrite.dstArrayElement = ;
前两个字段指定描述符集合更新和绑定的设置。我们为 uniform buffer 绑定的索引设定为0。描述符可以是数组,所以我们需要指定要更新的数组索引。在这里没有使用数组,所以简单的设置为0。
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = ;
我们在这里再一次指定描述符类型。可以通过数组一次性更新多个描述符,使用dstArrayElement起始索引。descriptorCount字段描述多少描述符需要被更新。
descriptorWrite.pBufferInfo = &bufferInfo;
descriptorWrite.pImageInfo = nullptr; // Optional
descriptorWrite.pTexelBufferView = nullptr; // Optional
最后的字段引用descriptorCount结构体的数组,它配置了实际的描述符。它的类型根据实际需要的三个描述符类型来设定。pBufferInfo字段用于指定描述符引用的缓冲区数据,pImageInfo字段用于指定描述符引用的图像数据,描述符使用pTexelBufferView引用缓冲区视图。我们的描述符是基于缓冲区的,所以我们使用pBufferInfo。
vkUpdateDescriptorSets(device, , &descriptorWrite, , nullptr);
使用vkUpdateDescriptorSets应用实际的更新。它接受两种数组的参数:一个数组是VkWriteDescriptorSet,另一个是VkCopyDescriptorSet。后一个数组可以用于两个描述符之间进行拷贝操作。
Using a descriptor set
我们现在需要更新createCommandBuffers函数,使用cmdBindDescriptorSets将描述符集合绑定到实际的着色器的描述符中:
vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, , , &descriptorSet, , nullptr);
与顶点和索引缓冲区不同,描述符集合不是图形管线唯一的。因此,我们需要指定是否要将描述符集绑定到图形或者计算管线。下一个参数是描述符所基于的布局。接下来的三个参数指定首个描述符的索引,要绑定的集合的数量以及要绑定的集合的数组。我们稍后回来。最后两个参数指定用于动态描述符的偏移数组。我们在后续的章节中会看到这些。
如果此时运行程序,会看不到任何内容在屏幕上。问题在于,由于我们在投影矩阵中进行了Y-flip操作,所以顶点现在以顺时针顺序而不是逆时针顺序绘制。这导致背面剔除以防止任何背面的集合体被绘制。来到createGraphicsPipeline函数,修改VkPipelineRasterizationStateCreateInfo结构体的frontFace如下:
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
运行程序如下:

矩形已经变为正方形,因为投影矩阵现在修正了宽高比。updateUniformData需要考虑屏幕的尺寸大小变化,所以我们不需要重新创建描述符集合在recreateSwapChain中。
Multiple descriptor sets
正如某些结构体和函数调用时候的提示所示,实际上可以绑定多个描述符集合。你需要在管线创建布局的时候为每个描述符集合指定描述符布局。着色器可以引用具体的描述符集合如下:
layout(set = , binding = ) uniform UniformBufferObject { ... }
我们可以使用此功能将每个对象和发生变化的描述符分配到单独的描述符集合中,在这种情况下,可以避免重新绑定大部分描述符,而这些描述符可能会更有效率。
项目代码 GitHub地址。
Vulkan Tutorial 24 Descriptor pool and sets的更多相关文章
- Vulkan Tutorial 23 Descriptor layout and buffer
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们现在可以将任意属性传递给每个顶点的顶点着色器使用.但是 ...
- Vulkan SDK 之 Descriptor Set Layouts and Pipeline Layouts
当我们有了一个uniform buff之后,vulkan 还不知道这个信息,需要通过descriptor进行描述. Descriptors and Descriptor Sets A descript ...
- [译]Vulkan教程(24)索引buffer
[译]Vulkan教程(24)索引buffer Index buffer 索引buffer Introduction 入门 The 3D meshes you'll be rendering in a ...
- Vulkan Tutorial 16 Command buffers
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 诸如绘制和内存操作相关命令,在Vulkan中不是通过函数直接调用的.我们需要在命令缓 ...
- Vulkan Tutorial 18 重构交换链
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 现在我们已经成功的在屏幕上绘制出三角形,但是在某些情况下, ...
- Vulkan Tutorial 28 Depth buffering
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 到目前为止,我们所使用的几何图形为3D,但仍然完全扁平的. ...
- Vulkan Tutorial 开发环境搭建之Windows
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 相信很多人在开始学习Vulkan开发的起始阶段都会在开发环境的配置上下一些功夫,那么 ...
- Vulkan Tutorial 02 编写Vulkan应用程序框架原型
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 General structure 在上一节中,我们创建了一个正确配置.可运行的的V ...
- Vulkan Tutorial 05 物理设备与队列簇
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Selecting a physical device 通过VkInstance初始 ...
随机推荐
- Git详细教程---多人协作开发
Git可以完成两件事情: 1. 版本控制 2.多人协作开发 如今的项目,规模越来越大,功能越来越多,需要有一个团队进行开发. 如果有多个开发人员共同开发一个项目,如何进行协作的呢. Git提供了一个非 ...
- java内存模型1
并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信 ...
- winows 服务器环境搭建 (碰到了windows服务器,小记一下吧~)
1.连接远程服务器 安装wamp 2.查看wamp 默认端口号是否与对应windows 服务器冲突,如果冲突,则改之 WAMP装好之后默认的端口是80,但是这个80端口呢,可以热门端口啊,迅雷,II ...
- 【转】典型的JavaScript面试题
问题1: 作用域(Scope) (function() { "use strict"; var a = b = 5; })(); console.log(b); 控制台(conso ...
- 在CentOS上使用Jexus托管运行 ZKEACMS
ZKEACMS Core 是基于 .net core 开发的,可以在 windows, linux, mac 上跨平台运行,接下来我们来看看如何在 CentOS 上使用Jexus托管运行 ZKEACM ...
- 详细介绍php中的命名空间
php命名空间的一个最明确的作用是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误.上一章节介绍了什么是php命名空间.php官网已很明确的进行了定义并形象化解释,这 ...
- OpenStack云平台的网络模式及其工作机制
网络,是OpenStack的部署中最容易出问题的,也是其结构中难以理清的部分.经常收到关于OneStack部署网络方面问题和OpenStack网络结构问题的邮件.下面根据自己的理解,谈一谈OpenSt ...
- js继承之借用构造函数继承
我的上一篇文章介绍了,原型链继承模式.但是单纯的原型链模式并不能很好地实现继承. 一.原型链的缺点 1.1 单纯的原型链继承最大的一个缺点,来自于原型中包含引用类型的值. 本来,我们没有通过原型链实现 ...
- springboot 集成shiro
首先看下shiro configuration 的配置,重要部分用红色标出了 package cn.xiaojf.today.shiro.configuration; import at.pollux ...
- ArrayList源码解读
在端午节这个节日里,有一个特殊的任务,我带着你一起揭开"ArrayList"的真面目.从成员变量.构造函数.主要方法三部分,对ArrayList有进一步的认识,希望能够帮助你. 一 ...