Search code examples
c99memory-alignmentvulkanvertex-buffer

Question regarding the memory alignment offset multiple for Vertex buffer data when calling vkCmdBindVertexBuffers()


I'm creating a Vulkan based renderer backend for my game framework. At the moment I'm loading in a mesh with around 10,000 unique triangles (not indexed - all individual) where each vertex has a position value, RGB value, no normals and no texture coords. This works out as 72 bytes per triangle, ie. 1 * xyz floats + 1 * RGB floats = 6 floats per vertex. 6 * 3 vertices = 18 floats per triangle. 18 * 4 = 72 bytes per triangle. The Vertex data is stored in a GPU local buffer with VK_BUFFER_USAGE_VERTEX_BUFFER_BIT flag set.

I'm also using the same vert and frag shaders for all meshes at the moment with push constants for the CPU calculated MVP matrix.

If I use multiples of 72 as for the offset param in vkCmdBindVertexBuffers(), then my mesh disintegrates in that the first triangles in the buffer are never drawn. I've incremented the offset by 72 frame by frame which dissolved the mesh with no segfaults or errors. LunarG standard validation is enabled with no reported validation errors, (I have a lot of error checking and logging in my code).

Incidentally, if I don't use multiples of 72 then I get some very interesting renders, but no crashes! I'm also getting a frame rate of 650fps on a six year old machine running in renderdoc.

This is the code that binds the vertex buffer...

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

Now, just because this runs fine on my PC doesn't mean that it's correct. One thing that I'm confused about is the area of the Vulkan spec regarding memory alignment requirements, specifically in VkPhysicalDeviceLimits.

There are several in VkPhysicalDeviceLimits:minTexelBufferOffsetAlignment, minUniformBufferOffsetAlignment & minStorageBufferOffsetAlignment.

The spec says: The alignment member satisfies the buffer descriptor offset alignment requirements associated with the VkBuffer’s usage:

If usage included VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT or VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, alignment must be an integer multiple of VkPhysicalDeviceLimits::minTexelBufferOffsetAlignment.

If usage included VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, alignment must be an integer multiple of VkPhysicalDeviceLimits::minUniformBufferOffsetAlignment.

If usage included VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, alignment must be an integer multiple of VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment.

I'm creating the Vertex buffer in device local memory with vkCreateBuffer() using bufferCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT (And VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT).

The question... 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)?

The reason I'm asking is that I want to store more than one mesh in a single vkCreateBuffer() allocated buffer with the VK_BUFFER_USAGE_VERTEX_BUFFER_BIT flag set. I can then offset into this 'super vertex buffer' for each unique mesh I need to draw, without having multiple Vertex buffer allocations. I know the limit for allocating Vertex buffers is usually 4096 (VkPhysicalDeviceLimits::maxMemoryAllocationCount) but rather than allocate multiple Vertex buffers I'd prefer to use one 'super buffer' for performance.

Does this make sense?

UPDATE: I've changed my code to use no offset in vkCmdBindVertexBuffers() and instead use the firstVertex param in vkCmdDraw() as a mesh model offset which produced a slightly higher and more stable FPS.


Solution

  • I'm not seeing any alignment requirements in the spec, though I think that's probably an oversight. You might try rounding to a multiple of 16; any actual alignment requirement is unlikely to be larger than that. So if your first mesh is 5 triangles, you need 5*72 bytes for it, and the second mesh would start at offset round_up(5*72, 16)=368. If that doesn't work, you probably have a bug elsewhere.

    Rather than using offsets to vkCmdBindVertexBuffers, though, you could just bind the full vertex buffer once, and use the firstVertex parameter for each draw to indicate the index into the buffer where the mesh starts.