## 要求
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;
参考资料
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于