【Vulkan】DescriptorSet(三)【Bindless】

发布于:2022-11-27 ⋅ 阅读:(635) ⋅ 点赞:(0)

Vulkan DescriptorSet(三)【Bindless】

Bindless

背景简介

传统管线的渲染流程,可以简化为:

BindFrameBuffer();      // FrameBuffer
BindProgram();          // Shader
SetStates();            // PipelineState
BindTexture();          // Shader resrouces
BindBuffer();           // Shader resrouces
BindVertexBuffer()      // Vertex Buffer
BindIndexBuffer();      // Index Buffer
Draw();                 // DrawCall (Linear / Indexed)

资源需要以 DrawCall 的粒度来组织,Vulkan Descriptor 的出现可以让资源绑定进行分组预处理,如按照 Frequency-Based 方式进行组织(set 序号越大更新越频繁),如:

  • set = 0:绑定 Global、Per-View、Per-Frame 资源
  • set = 1:绑定 Material 资源
  • set = 2:绑定 Per-Obeject,Per-Draw 资源

在这里插入图片描述

以上图为例,Shader 资源的绑定指令变成以下方式,这种组织方式可以有效的减少资源切换带来的开销,但是组织的粒度依旧围绕在单个 DrawCall。

// bindDescriptorSets(firstSet, pSets)
bindDescriptorSets(0, set[0, 1, 3])
draw();

bindDescriptorSets(2, set[4])
draw();

bindDescriptorSets(1, set[2, 5])
draw()

随着 DrawIndirect 方式出现,DrawCall 可以通过 Buffer 组织,IndirectDraw 可以单指令下发大量的 DrawCall,带来的典型挑战就是数据合并,其中 Texture 因为需要预先占用一个(slot,binding),需要更灵活的资源声明方式,因为 slot + binding 通常是规定的,变体的方式过于臃肿,数组或者 sparse resources 是一个不错的选择,这边先讨论数组。

Texture 数组的几种方式:

  • TextureArray
  • 固定数组
  • Runtime-sized 数组

对应 Shader 声明:

layout (set = 0, binding = 0) uniform texture2DArray textures;
layout (set = 0, binding = 1) uniform texture2D textures0[4];
layout (set = 0, binding = 2) uniform texture2D textures1[];
  • TextureArray: Format、Extent 等需要保持一致,限制太大
  • 固定数组:无 TextureArray 的限制,但是大小编译时确定,灵活性受限
  • Runtime-sized 数组:运行时指定

创建 Bindless DescriptorSet

主要步骤:

  • 查询 Feature 支持情况,需要使用 VkPhysicalDeviceFeatures2
VkPhysicalDeviceDescriptorIndexingFeatures phyIndexingFeatures = {};
phyIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;

VkPhysicalDeviceFeatures2 phyFeatures = {};
phyFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
phyFeatures.pNext = &phyIndexingFeatures;
vkGetPhysicalDeviceFeatures2(pDev, &phyFeatures);
  • 使能 Device Feature,主要涉及:
    runtimeDescriptorArray:允许支持 SPV 的 Runtime-sized 数组
    descriptorBindingVariableDescriptorCount:用于使能 VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
VkPhysicalDeviceDescriptorIndexingFeatures enabledIndexingFeature{};
enabledIndexingFeature.sType                                     = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;
enabledIndexingFeature.runtimeDescriptorArray                    = phyIndexingFeatures.runtimeDescriptorArray;
enabledIndexingFeature.descriptorBindingVariableDescriptorCount  = phyIndexingFeatures.descriptorBindingVariableDescriptorCount;

VkDeviceCreateInfo devInfo = {};
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.pNext = &enabledIndexingFeature;
  • 创建 DescriptorSetLayout,VkDescriptorSetLayoutBindingFlagsCreateInfo 使能 Layout 对于每个 LayoutBinding 设置 flag,主要涉及 VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
    引入几个限制
    • 无法使用 (UNIFORM / STORAGE) DYNAMIC 类型
    • 该 Bit 置位时,VkDescriptorSetLayoutBinding::descriptorCount 表示最大可能支持的 Descriptor 数量
    • 当前 LayoutBinding 必须保证 binding 号最大,即保证相关 Descriptor 位于队尾
VkDescriptorSetLayoutBindingFlagsCreateInfo setLayoutBindingFlags{};
setLayoutBindingFlags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
setLayoutBindingFlags.bindingCount = static_cast<uint32_t>(bindingFlags.size());
setLayoutBindingFlags.pBindingFlags = bindingFlags.data();

VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.pNext = &setLayoutBindingFlags;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings    = bindings.data();
  • 申请 DescriptorSet,需要通过 VkDescriptorSetVariableDescriptorCountAllocateInfo::pDescriptorCounts 信息,告知需要申请的实际使用的 Descriptor 数量。
VkDescriptorSetVariableDescriptorCountAllocateInfo variableDescriptorCountAllocInfo = {};
variableDescriptorCountAllocInfo.sType              = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO;
variableDescriptorCountAllocInfo.descriptorSetCount = 1;
variableDescriptorCountAllocInfo.pDescriptorCounts  = &variableDescriptors;

VkDescriptorSetAllocateInfo setInfo = {};
setInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
setInfo.pNext = &variableDescriptorCountAllocInfo;
  • 更新 DescriptorSet,按照使用情况将对应的 Buffer,Image,Sampler,BufferView 等更新到对应的 index。

基础示例

  • 使用 1 次 IndirectDraw,在屏幕绘制 4 个方块,每个方块分别使用一种 Material
  • 利用 bindless 特性在 (0, 2) 位置绑定 4 张格式、大小存在差异的纹理
  • 利用 storage buffer 合并 Material 参数
  • 利用 Per-Instance VertexBuffer 传递 ObjectID
    在这里插入图片描述

代码地址

  • 地址:sample/VulkanBindless/src/VulkanBindlessSample.cpp
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到