I am currently tasked with converting an OpenGL code base to Vulkan, but now stumbled over a problem with how this code uses vertex buffers, because it very dynamically alters the layout to address it as a large array of frames.
In order to render its animated models it sets up one large buffer containing all data for the entire model and calling the following function to set it all up:
void SetupVertexBufferFormat(unsigned int frame1, unsigned int frame2)
{
glVertexAttribPointer(PositionAttr, 3, GL_FLOAT, false, sizeof(Vertex), &vNull[frame1].x);
glVertexAttribPointer(TexcoordAttr, 2, GL_FLOAT, false, sizeof(Vertex), &vNull[frame1].u);
glVertexAttribPointer(Position2Attr, 3, GL_FLOAT, false, sizeof(Vertex), &vNull[frame2].x);
glVertexAttribPointer(NormalAttr, 4, GL_INT_2_10_10_10_REV, true, sizeof(Vertex), &vNull[frame1].packedNormal);
glVertexAttribPointer(Normal2Attr, 4, GL_INT_2_10_10_10_REV, true, sizeof(Vertex), &vNull[frame2].packedNormal);
}
Which is all nice and well on OpenGL but on Vulkan the vertex buffer layout is part of the pipeline object! Which means that porting the setup would require creating and destroying multiple pipelines per frame because the frame1 and frame2 values may be nearly randomly combined.
What cannot be done is altering the buffer's contents, which gets generated by the frontend that is off-limits because it still needs to work with the existing OpenGL backend.
Is there any way around it or is some complex pipeline management the only option here?
You seem to be conflating vertex format with vertex buffer bindings. glVertexAttrib
combines both in a single call, but you appear to have a single consistent vertex format that consists of two bindings, one with 3 attributes and the second with 2 attributes.
Take a look at some tutorials on separate vertex formats in OpenGL and try refactoring your GL backend to use that. The equivalent Vulkan pipeline setup should become more apparent.
The Vulkan vertex binding and attribute descriptions that corresponds to Ratchet's GL calls should look like this
std::vector<vk::VertexInputBindingDescription> bindingDescriptions = {
{ 0, sizeof(Vertex), vk::VertexInputRate::eVertex },
{ 1, sizeof(Vertex), vk::VertexInputRate::eVertex }
};
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions = {
{ PositionAttr, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x) },
{ TexcoordAttr, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u) },
{ NormalAttr, 0, vk::Format::eA2B10G10R10SnormPack32, offsetof(Vertex, packedNormal) },
{ Position2Attr, 1, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x) },
{ Normal2Attr, 1, vk::Format::eA2B10G10R10SnormPack32, offsetof(Vertex, packedNormal) },
};