操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Visual Studio 2017


Introduction

描述符布局描述了前一章节讨论过的可以绑定的描述符的类型。在本章节,我们创建描述符集,它将实际指定一个VkBuffer来绑定到一个uniform buffer描述符。

Descriptor pool


描述符集合不能直接创建,它们必须像命令缓冲区一样,从对象池中分配使用。对于描述符集合相当于调用描述符对象池。我们将写一个新的函数createDescriptorPool来配置。

  1. void initVulkan() {
  2. ...
  3. createUniformBuffer();
  4. createDescriptorPool();
  5. ...
  6. }
  7.  
  8. ...
  9.  
  10. void createDescriptorPool() {
  11.  
  12. }

我们首先需要明确我们使用的描述符集合包含的描述符类型与数量,这里使用VkDescriptorPoolSize结构体。

  1. VkDescriptorPoolSize poolSize = {};
  2. poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  3. poolSize.descriptorCount = ;

现在我们只有一个uniform buffer类型的单描述符。对象池大小将被VkDescriptorPoolCreateInfo结构体引用:

  1. VkDescriptorPoolCreateInfo poolInfo = {};
  2. poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  3. poolInfo.poolSizeCount = ;
  4. poolInfo.pPoolSizes = &poolSize;

我们也需要指定最大的描述符集合的分配数量:

  1. poolInfo.maxSets = ;

该结构体与命令对象池类似,有一些可选项用于决定每个描述符集合是否可以独立管理生命周期:VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT。创建完毕后我们不会进一步使用它,所以我们不需要该flag。在这里设置flags默认值为0

  1. VkDescriptorPool descriptorPool;
  2.  
  3. ...
  4.  
  5. if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
  6. throw std::runtime_error("failed to create descriptor pool!");
  7. }

添加新的类成员对象保存描述符对象池的句柄,通过调用vkCreateDescriptorPool创建它。与其他绘制资源一样,描述符对象池应该仅在程序退出的时候销毁:

  1. void cleanup() {
  2. cleanupSwapChain();
  3.  
  4. vkDestroyDescriptorPool(device, descriptorPool, nullptr);
  5.  
  6. ...
  7. }

Descriptor set


为了从对象池中分配描述符集合,我们需要添加一个createDescriptorSet函数:

  1. void initVulkan() {
  2. ...
  3. createDescriptorPool();
  4. createDescriptorSet();
  5. ...
  6. }
  7.  
  8. ...
  9.  
  10. void createDescriptorSet() {
  11.  
  12. }

描述符集合通过VkDescriptorSetAllocateInfo结构体描述具体的分配。需要指定用于分配的描述符对象池,分配的描述符集合数量,以及基于此的描述符布局:

  1. VkDescriptorSetLayout layouts[] = {descriptorSetLayout};
  2. VkDescriptorSetAllocateInfo allocInfo = {};
  3. allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  4. allocInfo.descriptorPool = descriptorPool;
  5. allocInfo.descriptorSetCount = ;
  6. allocInfo.pSetLayouts = layouts;

添加类成员存储描述符集合的句柄,并使用vkAllocateDescriptorSets分配:

  1. VkDescriptorPool descriptorPool;
  2. VkDescriptorSet descriptorSet;
  3.  
  4. ...
  5.  
  6. if (vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet) != VK_SUCCESS) {
  7. throw std::runtime_error("failed to allocate descriptor set!");
  8. }

我们不需要明确清理描述符集合,因为它们会在描述符对象池销毁的时候自动清理。调用vkAllocateDescriptorSets会分配一个具有uniform buffer描述符的描述符集合。

描述符集合已经分配了,但是内部的描述符需要配置。描述符需要引用缓冲区,就像uniform buffer描述符,使用VkDescriptorBufferInfo结构体进行配置。该结构体指定缓冲区和描述符内部包含的数据的区域:

  1. VkDescriptorBufferInfo bufferInfo = {};
  2. bufferInfo.buffer = uniformBuffer;
  3. bufferInfo.offset = ;
  4. bufferInfo.range = sizeof(UniformBufferObject);

描述符的配置更新使用vkUpdateDescriptorSets函数,它需要VkWriteDescriptorSet结构体的数组作为参数。

  1. VkWriteDescriptorSet descriptorWrite = {};
  2. descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  3. descriptorWrite.dstSet = descriptorSet;
  4. descriptorWrite.dstBinding = ;
  5. descriptorWrite.dstArrayElement = ;

前两个字段指定描述符集合更新和绑定的设置。我们为 uniform buffer 绑定的索引设定为0。描述符可以是数组,所以我们需要指定要更新的数组索引。在这里没有使用数组,所以简单的设置为0

  1. descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  2. descriptorWrite.descriptorCount = ;

我们在这里再一次指定描述符类型。可以通过数组一次性更新多个描述符,使用dstArrayElement起始索引。descriptorCount字段描述多少描述符需要被更新。

  1. descriptorWrite.pBufferInfo = &bufferInfo;
  2. descriptorWrite.pImageInfo = nullptr; // Optional
  3. descriptorWrite.pTexelBufferView = nullptr; // Optional

最后的字段引用descriptorCount结构体的数组,它配置了实际的描述符。它的类型根据实际需要的三个描述符类型来设定。pBufferInfo字段用于指定描述符引用的缓冲区数据,pImageInfo字段用于指定描述符引用的图像数据,描述符使用pTexelBufferView引用缓冲区视图。我们的描述符是基于缓冲区的,所以我们使用pBufferInfo

  1. vkUpdateDescriptorSets(device, , &descriptorWrite, , nullptr);

使用vkUpdateDescriptorSets应用实际的更新。它接受两种数组的参数:一个数组是VkWriteDescriptorSet,另一个是VkCopyDescriptorSet。后一个数组可以用于两个描述符之间进行拷贝操作。

Using a descriptor set


我们现在需要更新createCommandBuffers函数,使用cmdBindDescriptorSets将描述符集合绑定到实际的着色器的描述符中:

  1. vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, , , &descriptorSet, , nullptr);

与顶点和索引缓冲区不同,描述符集合不是图形管线唯一的。因此,我们需要指定是否要将描述符集绑定到图形或者计算管线。下一个参数是描述符所基于的布局。接下来的三个参数指定首个描述符的索引,要绑定的集合的数量以及要绑定的集合的数组。我们稍后回来。最后两个参数指定用于动态描述符的偏移数组。我们在后续的章节中会看到这些。

如果此时运行程序,会看不到任何内容在屏幕上。问题在于,由于我们在投影矩阵中进行了Y-flip操作,所以顶点现在以顺时针顺序而不是逆时针顺序绘制。这导致背面剔除以防止任何背面的集合体被绘制。来到createGraphicsPipeline函数,修改VkPipelineRasterizationStateCreateInfo结构体的frontFace如下:

  1. rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
  2. rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;

运行程序如下:

矩形已经变为正方形,因为投影矩阵现在修正了宽高比。updateUniformData需要考虑屏幕的尺寸大小变化,所以我们不需要重新创建描述符集合在recreateSwapChain中。

Multiple descriptor sets


正如某些结构体和函数调用时候的提示所示,实际上可以绑定多个描述符集合。你需要在管线创建布局的时候为每个描述符集合指定描述符布局。着色器可以引用具体的描述符集合如下:

  1. layout(set = , binding = ) uniform UniformBufferObject { ... }

我们可以使用此功能将每个对象和发生变化的描述符分配到单独的描述符集合中,在这种情况下,可以避免重新绑定大部分描述符,而这些描述符可能会更有效率。

项目代码 GitHub地址。

Vulkan Tutorial 24 Descriptor pool and sets的更多相关文章

  1. Vulkan Tutorial 23 Descriptor layout and buffer

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们现在可以将任意属性传递给每个顶点的顶点着色器使用.但是 ...

  2. Vulkan SDK 之 Descriptor Set Layouts and Pipeline Layouts

    当我们有了一个uniform buff之后,vulkan 还不知道这个信息,需要通过descriptor进行描述. Descriptors and Descriptor Sets A descript ...

  3. [译]Vulkan教程(24)索引buffer

    [译]Vulkan教程(24)索引buffer Index buffer 索引buffer Introduction 入门 The 3D meshes you'll be rendering in a ...

  4. Vulkan Tutorial 16 Command buffers

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 诸如绘制和内存操作相关命令,在Vulkan中不是通过函数直接调用的.我们需要在命令缓 ...

  5. Vulkan Tutorial 18 重构交换链

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 现在我们已经成功的在屏幕上绘制出三角形,但是在某些情况下, ...

  6. Vulkan Tutorial 28 Depth buffering

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 到目前为止,我们所使用的几何图形为3D,但仍然完全扁平的. ...

  7. Vulkan Tutorial 开发环境搭建之Windows

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 相信很多人在开始学习Vulkan开发的起始阶段都会在开发环境的配置上下一些功夫,那么 ...

  8. Vulkan Tutorial 02 编写Vulkan应用程序框架原型

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 General structure 在上一节中,我们创建了一个正确配置.可运行的的V ...

  9. Vulkan Tutorial 05 物理设备与队列簇

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Selecting a physical device 通过VkInstance初始 ...

随机推荐

  1. Ajax理解总结

    前端开发拿数据页面实时更新是离不开Ajax这个技术的 AJAX即"Asynchronous Javascript And XML"(异步JavaScript和XML),是指一种创建 ...

  2. linux防火墙基本操作

    1.查看防火墙运行状态 # firewall-cmd --state 或者 # systemctl status firewalld.service .关闭防火墙 # systemctl stop f ...

  3. CountDownLacth详解

    一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 用给定的计数 初始化 CounDownLatch.由于调用了countDown() 方法,所以在当前计数到达零之 ...

  4. 详解Google Chrome浏览器(操作篇)(下)

    开篇概述 由于最近忙于公司产品的架构与研发,已经三个多月没有写博客了,收到有些朋友的来信,问为什么不及时更新博客内容呢,他们说他们正期待着某些内容.对此,非常抱歉,那么我在此也给各位朋友一些承诺,从即 ...

  5. 数据结构与算法系列研究七——图、prim算法、dijkstra算法

    图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph ...

  6. 软件工程期末考试 AHNU

    1.      数据流图:一种图形化技术,它描绘信息流和数据从输入移动到输出的过程中所经受的变换.在数据流图中没有任何具体的物理部件,它只是描绘数据在软件中流动和被处理的逻辑过程,是系统逻辑功能的图形 ...

  7. 同步文件的利器-rsync

    即使你只是个人用户而不是一个企业,备份你自己的数据也是非常重要的,我不想失去任何这些数据. rsync是同步文件的利器,一般用于多个机器之间的文件同步与备份,同时也支持在本地的不同目录之间互相同步文件 ...

  8. 你真的了解WebSocket吗?

    WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex ...

  9. java中为什么实体类需要实现序列化

    当客户端访问某个能开启会话功能的资源,web服务器就会创建一个HTTPSession对象,每个HTTPSession对象都会占用一定的内存,如果在同一个时间段内访问的用户太多,就会消耗大量的服务器内存 ...

  10. tab切换插件开发

    我开发的tab切换插件,基于jquery库,实现tab标签页的切换.插件的名称为jquery.tabSwitch.js. 插件实现代码如下: ; (function ($) { $.fn.tabSwi ...