更新描述符集
译者注:示例代码点击此处
我们已创建了一个描述符池并分配了描述符集。由于创建了布局,我们知道它们的内部结构。现在我们要提供特定的资源(采集器,图像视图,缓冲区或缓冲区视图),这些资源应该稍后通过描述符集绑定到管道。定义应该使用的资源是通过更新描述符集的过程完成的。
做好准备
更新描述符集要求我们为流程中涉及的每个描述符提供大量数据。而且,提供的数据取决于描述符的类型。
对于采样器(samplers)和各种图像(image)描述符,使用具有以下定义的ImageDescriptorInfo类型:
struct ImageDescriptorInfo {
VkDescriptorSet TargetDescriptorSet;
uint32_t TargetDescriptorBinding;
uint32_t TargetArrayElement;
VkDescriptorType TargetDescriptorType;
std::vector<VkDescriptorImageInfo> ImageInfos;
};
对于统一(uniform)和存储缓冲区(storage buffers )(及其动态变化),使用BufferDescriptorInfo类型。 它有以下定义:
struct BufferDescriptorInfo {
VkDescriptorSet TargetDescriptorSet
uint32_t TargetDescriptorBinding
uint32_t TargetArrayElement
VkDescriptorType TargetDescriptorType
std::vector<VkDescriptorBufferInfo> BufferInfos
};
对于统一(uniform)和存储纹理缓冲区(storage texel buffers,),引入了TexelBufferDescriptorInfo类型,其定义如下:
struct TexelBufferDescriptorInfo {
VkDescriptorSet TargetDescriptorSet;
uint32_t TargetDescriptorBinding;
uint32_t TargetArrayElement;
VkDescriptorType TargetDescriptorType;
std::vector<VkBufferView> TexelBufferViews;
};
当我们想要用新描述符的句柄(尚未绑定)更新描述符集时,使用前面的结构。还可以从其他已更新的集合中赋值描述符数据,为此,使用了CopyDescriptorInfo类型,定义如下:
struct CopyDescriptorInfo {
VkDescriptorSet TargetDescriptorSet;
uint32_t TargetDescriptorBinding;
uint32_t TargetArrayElement;
VkDescriptorSet SourceDescriptorSet;
uint32_t SourceDescriptorBinding;
uint32_t SourceArrayElement;
uint32_t DescriptorCount;
};
所有前面的结构定义了应该更新的描述符集的句柄、给定集合中描述符的索引、以及如果我们想要更新通过数组访问的描述符的数组索引。 其余参数是特定的类型。
怎么做...
- 使用逻辑设备的句柄初始化名为logical_device的VkDevice类型的变量。
- 创建一个名为write_descriptors的std::vector<VkWriteDescriptorSet>类型变量,为每个应更新的新描述符添加一个新元素,并为其成员使用一下值:
·sType为VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET
·pNext为nullptr
·dstSet为应更新的描述符集句柄
·dstBinding为指定集描述符的索引(绑定)
·dstArrayElement为数组的开始索引,如果给定的描述符是通过着色器内的数组访问的,则应从中更新描述符(否则为0值)。
·descriptorCount为更新的描述符数(pImageInfo、pBufferInfo或pTexelBufferView数组中的元素数)
·descriptorType为描述符的类型
·在采样器(samplers)和图像(image)描述符的情况下,为descriptorCount设置描述符数量,并在pImageInfo中提供指向其第一个元素的指针(将pBufferInfo和pTexelBufferView成员设置为nullptr)。对每个数组元素使用以下值:
·sampler为采样器和组合图像采样器描述符的采样器句柄
·imageView为采样图像、存储图像、组合图像采样器和输出附件描述符、图像视图句柄。
·ImageLayout为当图像着色器访问描述符时,给定图像将处于的布局。
·在统一或存储缓冲区(及其动态)的情况下,为descriptorCount设置描述符数量,并在pBufferInfo中提供指向其第一个元素的指针(将pImageInfo和pTexelBufferView成员设置为nullptr),并为每个数组元素使用以下值:
·buffer为缓冲区句柄
·offset为缓冲区内的内存偏移量(或动态描述符的基本偏移量)
·range为应该用于给定描述符的缓冲区的内存大小
·对于均匀纹理像素缓冲区或存储纹理元素缓冲区,为descriptorCount设置描述符数量,并在pTexelBufferView中提供指向其第一个元素的指针(将pImageInfo和pBufferInfo成员设置为nullptr)。 - 创建一个名为copy_descriptors的std::vector<VkCopyDescriptorSet>类型的变量。为每个描述符数据添加一个元素到该向量,该数据应该从另一个已经更新的描述符中复制。对每个新元素的成员使用以下值:
·sType为VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET
·pNext为nullptr
·srcSet为应从中复制数据的描述符集句柄
·srcBinding为源描述符集中的绑定编号
·srcArrayElement为源描述符集中的数组索引
·dstSet为应更新数据到其中的描述符集句柄
·dstBinding为目标描述符中的绑定编号
·dstArrayElement为目标描述符集中的数组索引
·descriptorCount为从源集复制并在目标中更新的描述符数量。 - 调用vkUpdateDescriptorSets( logical_device, static_cast<uint32_t>(write_descriptors.size()),
&write_descriptors[0], static_cast<uint32_t>(copy_descriptors.size()), ©_descriptors[0] )并提供logical_device变量、write_descriptors向量中的元素数、指向write_descriptors第一个元素的指针、copy_descriptors向量中的元素数量、以及指向copy_descriptors向量的第一个元素的指针。
这个怎么运作...
更新描述符集会导致指定的资源(采样器,图像视图,缓冲区或缓冲区视图)填充指定集中的条目。当更新集绑定到管线时,可以通过着色器访问这些资源。
我们可以将新的(或未使用的)资源写入描述符集。在下面的示例中,我们通过使用“做好准备”部分提到的自定结构体来执行操作:
std::vector<VkWriteDescriptorSet> write_descriptors;
for( auto & image_descriptor : image_descriptor_infos ) {
write_descriptors.push_back( {
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType
nullptr, // const void * pNext
image_descriptor.TargetDescriptorSet, // VkDescriptorSet dstSet
image_descriptor.TargetDescriptorBinding, // uint32_t dstBinding
image_descriptor.TargetArrayElement, // uint32_t dstArrayElement
static_cast<uint32_t>(image_descriptor.ImageInfos.size()), // uint32_t descriptorCount
image_descriptor.TargetDescriptorType, // VkDescriptorType descriptorType
image_descriptor.ImageInfos.data(), // const VkDescriptorImageInfo * pImageInfo
nullptr, // const VkDescriptorBufferInfo * pBufferInfo
nullptr // const VkBufferView * pTexelBufferView
} );
}
for( auto & buffer_descriptor : buffer_descriptor_infos ) {
write_descriptors.push_back( {
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType
nullptr, // const void * pNext
buffer_descriptor.TargetDescriptorSet, // VkDescriptorSet dstSet
buffer_descriptor.TargetDescriptorBinding, // uint32_t dstBinding
buffer_descriptor.TargetArrayElement, // uint32_t dstArrayElement
static_cast<uint32_t>(buffer_descriptor.BufferInfos.size()), // uint32_t descriptorCount
buffer_descriptor.TargetDescriptorType, // VkDescriptorType descriptorType
nullptr, // const VkDescriptorImageInfo * pImageInfo
buffer_descriptor.BufferInfos.data(), // const VkDescriptorBufferInfo * pBufferInfo
nullptr // const VkBufferView * pTexelBufferView
} );
}
for( auto & texel_buffer_descriptor : texel_buffer_descriptor_infos ) {
write_descriptors.push_back( {
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType
nullptr, // const void * pNext
texel_buffer_descriptor.TargetDescriptorSet, // VkDescriptorSet dstSet
texel_buffer_descriptor.TargetDescriptorBinding, // uint32_t dstBinding
texel_buffer_descriptor.TargetArrayElement, // uint32_t dstArrayElement
static_cast<uint32_t>(texel_buffer_descriptor.TexelBufferViews.size()), // uint32_t descriptorCount
texel_buffer_descriptor.TargetDescriptorType, // VkDescriptorType descriptorType
nullptr, // const VkDescriptorImageInfo * pImageInfo
nullptr, // const VkDescriptorBufferInfo * pBufferInfo
texel_buffer_descriptor.TexelBufferViews.data() // const VkBufferView * pTexelBufferView
} );
}
我们还可以重用其他集合中的描述符。复制已填充的描述符应该比编写描述符更快。可以这样做:
std::vector<VkCopyDescriptorSet> copy_descriptors;
for( auto & copy_descriptor : copy_descriptor_infos ) {
copy_descriptors.push_back( {
VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, // VkStructureType sType
nullptr, // const void * pNext
copy_descriptor.SourceDescriptorSet, // VkDescriptorSet srcSet
copy_descriptor.SourceDescriptorBinding, // uint32_t srcBinding
copy_descriptor.SourceArrayElement, // uint32_t srcArrayElement
copy_descriptor.TargetDescriptorSet, // VkDescriptorSet dstSet
copy_descriptor.TargetDescriptorBinding, // uint32_t dstBinding
copy_descriptor.TargetArrayElement, // uint32_t dstArrayElement
copy_descriptor.DescriptorCount // uint32_t descriptorCount
} );
}
更新描述符集的操作是通过单个函数调用执行的:
vkUpdateDescriptorSets( logical_device, static_cast<uint32_t>(write_descriptors.size()), write_descriptors.data(), static_cast<uint32_t>(copy_descriptors.size()), copy_descriptors.data() );