Reputation: 83
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?
Upvotes: 1
Views: 1839
Reputation: 29240
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) },
};
Upvotes: 2
Reputation: 48216
To expand on JHerico's answer.
If you use separate vertex attributes from opengl you get this form:
glVertexAttribFormat( PositionAttr, 3, GL_FLOAT, false, offsetof(Vertex, x));
glVertexAttribBinding(PositionAttr, 1);
glVertexAttribFormat( TexcoordAttr, 2, GL_FLOAT, false, offsetof(Vertex, u));
glVertexAttribBinding(TexcoordAttr, 1);
glVertexAttribFormat( NormalAttr, 4, GL_INT_2_10_10_10_REV, true, offsetof(Vertex, packedNormal));
glVertexAttribBinding(NormalAttr, 1);
glVertexAttribFormat( Position2Attr, 3, GL_FLOAT, false, offsetof(Vertex, x));
glVertexAttribBinding(Position2Attr, 2);
glVertexAttribFormat( Normal2Attr, 4, GL_INT_2_10_10_10_REV, true, offsetof(Vertex, packedNormal));
glVertexAttribBinding(Normal2Attr, 2);
Then when rendering you set the offset for binding 1 to the offset of frame 1 and binding 2 to the offset of frame 2.
void SetupVertexBufferFormat(unsigned int frame1, unsigned int frame2) {
glBindVertexBuffer(1, vbo, &vNull[frame1], sizeof(Vertex));
glBindVertexBuffer(2, vbo, &vNull[frame2], sizeof(Vertex));
}
This you can directly translate to vulkan's state.
Upvotes: 1