描述符动态偏移

Vulkan 提供两种类型的描述符,允许在绑定时调整偏移量,如规范中所定义

  • 动态统一缓冲区 (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)

  • 动态存储缓冲区 (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)

示例

此示例将拥有 32 字节的缓冲区,其中 16 个字节将在 vkUpdateDescriptorSets 时设置。 在第一个示例中,我们将不添加任何动态偏移。

VkDescriptorSet descriptorSet; // allocated
VkBuffer buffer; // size of 32 bytes

VkDescriptorBufferInfo bufferInfo = {
    buffer,
    4,      // offset
    16      // range
};

VkWriteDescriptorSet writeInfo = {
    .dstSet = descriptorSet,
    .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC,
    .pBufferInfo = bufferInfo
};

vkUpdateDescriptorSets(
    1,         // descriptorWriteCount,
    &writeInfo // pDescriptorWrites,
);

// No dynamic offset
vkCmdBindDescriptorSets(
    1,              // descriptorSetCount,
    &descriptorSet, // pDescriptorSets,
    0,              // dynamicOffsetCount
    NULL            // pDynamicOffsets
);

我们的缓冲区现在看起来如下所示

descriptor_dynamic_offset_example_a.png

接下来,将在绑定时应用 8 字节的动态偏移。

uint32_t offsets[1] = { 8 };
vkCmdBindDescriptorSets(
    1,              // descriptorSetCount,
    &descriptorSet, // pDescriptorSets,
    1,              // dynamicOffsetCount
    offsets         // pDynamicOffsets
);

我们的缓冲区现在看起来如下所示

descriptor_dynamic_offset_example_b.png

使用 VK_WHOLE_SIZE 的示例

这次,将使用 VK_WHOLE_SIZE 值作为范围。 除了 VkDescriptorBufferInfo::range 之外,一切都与上面的示例相同。

VkDescriptorSet descriptorSet; // allocated
VkBuffer buffer; // size of 32 bytes

VkDescriptorBufferInfo info = {
    buffer,
    4,             // offset
    VK_WHOLE_SIZE  // range
};

VkWriteDescriptorSet writeInfo = {
    .dstSet = descriptorSet,
    .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC,
    .pBufferInfo = bufferInfo
};

vkUpdateDescriptorSets(
    1,         // descriptorWriteCount,
    &writeInfo // pDescriptorWrites,
);

// No dynamic offset
vkCmdBindDescriptorSets(
    1,              // descriptorSetCount,
    &descriptorSet, // pDescriptorSets,
    0,              // dynamicOffsetCount
    NULL            // pDynamicOffsets
);

我们的缓冲区现在看起来如下所示

descriptor_dynamic_offset_example_c.png

这次,如果我们尝试应用动态偏移,将会遇到未定义的行为,并且验证层会给出错误

// Invalid
uint32_t offsets[1] = { 8 };
vkCmdBindDescriptorSets(
    1,              // descriptorSetCount,
    &descriptorSet, // pDescriptorSets,
    1,              // dynamicOffsetCount
    offsets         // pDynamicOffsets
);

这是带有无效动态偏移的样子

descriptor_dynamic_offset_example_d.png

限制

检查 minUniformBufferOffsetAlignmentminStorageBufferOffsetAlignment 也很重要,因为基本偏移量和动态偏移量都必须是这些限制的倍数。