c99 - 关于调用 vkCmdBindVertexBuffers() 时顶点缓冲区数据的内存对齐偏移倍数的问题

标签 c99 memory-alignment vulkan vertex-buffer

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

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

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

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

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

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

现在,仅仅因为它在我的 PC 上运行良好并不意味着它是正确的。我感到困惑的一件事是 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)在设备本地内存中创建顶点缓冲区。

问题... As I'm not creating a buffer with VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT or VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, does that mean that there are no memory alignment requirements for the offset parameter when I call vkCmdBindVertexBuffers(cmd, 0, 1, vertexBuffer, offsets)?

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

这有意义吗?

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

最佳答案

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

不过,与其使用 vkCmdBindVertexBuffers 的偏移量,您可以只绑定(bind)一次完整的顶点缓冲区,并为每次绘制使用 firstVertex 参数来指示索引到网格开始的缓冲区。

关于c99 - 关于调用 vkCmdBindVertexBuffers() 时顶点缓冲区数据的内存对齐偏移倍数的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54542982/

相关文章:

c++ - 如何为我的 Vulkan 类创建这个通用数据结构字段?

c - 何时在 C 中使用可变长度数组,但何时使用动态分配?

c - 赋值运算符和序列点的副作用

c - 声明数组和使用 malloc 时出现 ISO C90 错误

c - 如何在没有 setenv() 的情况下在 ISO c99 标准 C 中设置环境变量?

c++ - 为什么我不能为 C++ 枚举类型变量分配正确的值?

c++ - Vulkan:转储当前分配的对象?

glsl - 尝试在计算着色器中同步计数器 SSBO 的增量

C 指针可以与任何其他数据类型对齐吗?

c - 结构成员对齐——使用 16 位和 32 位编译器的不同 sizeof