首页 > 解决方案 > 关于调用 vkCmdBindVertexBuffers() 时顶点缓冲区数据的内存对齐偏移倍数的问题

问题描述

我正在为我的游戏框架创建一个基于 Vulkan 的渲染器后端。目前,我正在加载一个包含大约 10,000 个唯一三角形(未编入索引 - 全部单独)的网格,其中每个顶点都有一个位置值、RGB 值、没有法线和纹理坐标。这相当于每个三角形 72 个字节,即。1 * xyz 浮点数 + 1 * RGB 浮点数 = 每个顶点 6 个浮点数。6 * 3 个顶点 = 每个三角形 18 个浮点数。18 * 4 = 每个三角形 72 个字节。顶点数据存储在设置了 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT 标志的 GPU 本地缓冲区中。

目前,我还在为所有网格使用相同的顶点和片段着色器,并为 CPU 计算的 MVP 矩阵使用推送常量。

如果我使用 72 的倍数作为 vkCmdBindVertexBuffers() 中的偏移参数,那么我的网格会分解,因为缓冲区中的第一个三角形永远不会被绘制。我已经将偏移量逐帧增加了 72 帧,从而在没有段错误或错误的情况下溶解了网格。LunarG 标准验证已启用,没有报告验证错误,(我的代码中有很多错误检查和日志记录)。

顺便说一句,如果我不使用 72 的倍数,那么我会得到一些非常有趣的渲染,但不会崩溃!我还在一台运行在 renderdoc 中的六岁机器上获得了 650fps 的帧速率。

这是绑定顶点缓冲区的代码...

vkCmdBindVertexBuffers, (cmd[swapindex], 0, 1, vertexBuffers, offsets)

现在,仅仅因为这在我的电脑上运行良好并不意味着它是正确的。我感到困惑的一件事是 Vulkan 规范中关于内存对齐要求的领域,特别是在 VkPhysicalDeviceLimits 中。

VkPhysicalDeviceLimits中有几个:minTexelBufferOffsetAlignment、minUniformBufferOffsetAlignment & minStorageBufferOffsetAlignment。

规范说:对齐成员满足与 VkBuffer 使用相关的缓冲区描述符偏移对齐要求:

如果使用包括 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT 或 VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,对齐必须是 VkPhysicalDeviceLimits::minTexelBufferOffsetAlignment 的整数倍。

如果使用包括 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,对齐必须是 VkPhysicalDeviceLimits::minUniformBufferOffsetAlignment 的整数倍。

如果使用包括 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,对齐必须是 VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment 的整数倍。

我正在使用 vkCreateBuffer() 使用 bufferCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT (和 VK_BUFFER_USAGE_TRANSFER_DST_BIT,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)在设备本地内存中创建顶点缓冲区。

问题...由于我没有使用 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT、VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT、VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT 或 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT 创建缓冲区,这是否意味着当我调用 vkCmdBindVertexBuffers、offset、 )?

我问的原因是我想在一个 vkCreateBuffer() 分配的缓冲区中存储多个网格,并设置了 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT 标志。然后,我可以为需要绘制的每个唯一网格偏移到这个“超级顶点缓冲区”,而无需分配多个顶点缓冲区。我知道分配顶点缓冲区的限制通常是 4096 (VkPhysicalDeviceLimits::maxMemoryAllocationCount),但我宁愿使用一个“超级缓冲区”来提高性能,而不是分配多个顶点缓冲区。

这有意义吗?

更新:我已将代码更改为在 vkCmdBindVertexBuffers() 中不使用偏移量,而是使用 vkCmdDraw() 中的 firstVertex 参数作为网格模型偏移量,从而产生稍高且更稳定的 FPS。

标签: c99memory-alignmentvulkanvertex-buffer

解决方案


我在规范中没有看到任何对齐要求,尽管我认为这可能是一个疏忽。您可以尝试四舍五入到 16 的倍数;任何实际的对齐要求都不太可能大于此。因此,如果您的第一个网格是 5 个三角形,则需要 5*72 字节,第二个网格将从偏移量 round_up(5*72, 16)=368 开始。如果这不起作用,您可能在其他地方有错误。

vkCmdBindVertexBuffers但是,您可以只绑定一次完整的顶点缓冲区,然后使用firstVertex每次绘制的参数来指示网格开始的缓冲区的索引,而不是使用偏移量。


推荐阅读