Multi Indirect Draw

## 要求

MID 设备需要支持 VK_KHR_buffer_device_address 扩展,根据官方的文档,Vulkan 1.3 才强制支持,1.2 扩展为可选项。

顺便贴下 Android 版本发布时间和要求的 Vulkan 版本信息,信息来源 Android 官网和网络,详见参考资料。

搭载 Android 13 及更高版本的设备应支持 Vulkan 1.3。

搭载 Android 10 的设备应支持 Vulkan 1.1。

其他设备可以选择支持 Vulkan 1.3、1.2 和 1.1。

版本名称 版本 发布时间 对应 API
Q (Android Q) 10.0 2019.9.4 API Level 29
11 (Android R) 11.0 2020.9.9 API Level 30
12 (Android S) 12.0 2021.5.19 API Level 31
13 (Android T) 13.0 2022.8.16 API Level 32

Android 10.0 开始,设备必须支持 Vulkan 1.1。

在创建 VkDevice 时需要启用 buffer device address 扩展,同时主动打开 multiDrawIndirect ​特性。在编写 Demo 程序时,只启用了 buffer device address 扩展,未设置 enabled features,导致 API 验证层报错。

let extensions = [
    khr::buffer_device_address::NAME.as_ptr(),
];

let enabled_features = vk::PhysicalDeviceFeatures::default()
    .multi_draw_indirect(true);

let info = vk::DeviceCreateInfo::default()
    .queue_create_infos(&family_info)
    .enabled_extension_names(&extensions)
    .enabled_features(&enabled_features);

绘制命令

普通绘制命令

普通绘制命令直接在命令中指定顶点数,实例数和数据相关的偏移,在 CPU 端组织 VkCommandBuffer 时即已确实。并且虽然可以通过 GPU Instancing 技术完成多模型在同 Draw Call 中绘制,但虽然只有一份 vetexOffset 数据,所以只能完成相同模型的 GPU Instancing。

// Provided by VK_VERSION_1_0
void vkCmdDraw(
    VkCommandBuffer                             commandBuffer,
    uint32_t                                    vertexCount,
    uint32_t                                    instanceCount,
    uint32_t                                    firstVertex,
    uint32_t                                    firstInstance);
// Provided by VK_VERSION_1_0
void vkCmdDrawIndexed(
    VkCommandBuffer                             commandBuffer,
    uint32_t                                    indexCount,
    uint32_t                                    instanceCount,
    uint32_t                                    firstIndex,
    int32_t                                     vertexOffset,
    uint32_t                                    firstInstance);

Indirect 绘制命令

间接绘制命令从 VkBuffer 中读取具体的绘制参数,而非直接在命令中指定。如此即可在 GPU 中完成类如视锥裁剪,计算出相关的绘制参数存放到 VkBuffer 中,等到具体绘制时,再从 VkBuffer 中读取。传统的绘制命令是无法完成此工作。

同时可以注意到,vertexOffset 也是参数的一部分,那么可以做到不同模型的 GPU Instancing。

// Provided by VK_VERSION_1_0
void vkCmdDrawIndirect(
    VkCommandBuffer                             commandBuffer,
    VkBuffer                                    buffer,
    VkDeviceSize                                offset,
    uint32_t                                    drawCount,
    uint32_t                                    stride);

// Provided by VK_VERSION_1_0
typedef struct VkDrawIndirectCommand {
    uint32_t    vertexCount;
    uint32_t    instanceCount;
    uint32_t    firstVertex;
    uint32_t    firstInstance;
} VkDrawIndirectCommand;
// Provided by VK_VERSION_1_0
void vkCmdDrawIndexedIndirect(
    VkCommandBuffer                             commandBuffer,
    VkBuffer                                    buffer,
    VkDeviceSize                                offset,
    uint32_t                                    drawCount,
    uint32_t                                    stride);

// Provided by VK_VERSION_1_0
typedef struct VkDrawIndexedIndirectCommand {
    uint32_t    indexCount;
    uint32_t    instanceCount;
    uint32_t    firstIndex;
    int32_t     vertexOffset;
    uint32_t    firstInstance;
} VkDrawIndexedIndirectCommand;

Indirect Buffer

Vulkan 专门存在一种类型的 VkBuffer 适用于 Indirect 命令,在绑定 VkBuffer 给 Indirect 命令时需要注意。

VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT specifies that the buffer is suitable for passing as the buffer parameter to vkCmdDrawIndirect, vkCmdDrawIndexedIndirect, vkCmdDrawMeshTasksIndirectNV, vkCmdDrawMeshTasksIndirectCountNV, vkCmdDrawMeshTasksIndirectEXT, vkCmdDrawMeshTasksIndirectCountEXT, vkCmdDrawClusterIndirectHUAWEI, or vkCmdDispatchIndirect. It is also suitable for passing as the buffer member of VkIndirectCommandsStreamNV, or sequencesCountBuffer or sequencesIndexBuffer or preprocessedBuffer member of VkGeneratedCommandsInfoNV. It is also suitable for passing as the underlying buffer of either the preprocessAddress or sequenceCountAddress members of VkGeneratedCommandsInfoEXT.

VK_EXT_multi_draw

Vulkan 还提供了 Multi Draw 的扩展,可以实现类似的功能,但相关绘制参数只能在命令录制时指定,无法使用 Computer Shader 计算得出。与 Indirect 版本相似,但不需要 GPU-accessible memory。

// Provided by VK_EXT_multi_draw
void vkCmdDrawMultiIndexedEXT(
    VkCommandBuffer                             commandBuffer,
    uint32_t                                    drawCount,
    const VkMultiDrawIndexedInfoEXT*            pIndexInfo,
    uint32_t                                    instanceCount,
    uint32_t                                    firstInstance,
    uint32_t                                    stride,
    const int32_t*                              pVertexOffset);

// Provided by VK_EXT_multi_draw
typedef struct VkMultiDrawIndexedInfoEXT {
    uint32_t    firstIndex;
    uint32_t    indexCount;
    int32_t     vertexOffset;
} VkMultiDrawIndexedInfoEXT;

参考资料

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...