Vulkan 中的动态顶点缓冲区格式设置
Dynamic vertex buffer format setup in Vulkan
我目前的任务是将 OpenGL 代码库转换为 Vulkan,但现在无意中发现了此代码如何使用顶点缓冲区的问题,因为它非常动态地改变布局以将其作为大量帧来处理。
为了渲染它的动画模型,它设置了一个包含整个模型所有数据的大缓冲区,并调用以下函数来设置它:
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);
}
这在 OpenGL 上很好,但在 Vulkan 上,顶点缓冲区布局是管道对象的一部分!这意味着移植设置需要每帧创建和销毁多个管道,因为 frame1 和 frame2 值可能几乎是随机组合的。
不能做的是改变缓冲区的内容,这是由前端生成的,这是禁止的,因为它仍然需要与现有的 OpenGL 后端一起工作。
是否有任何解决方法,或者一些复杂的管道管理是这里唯一的选择吗?
您似乎将顶点格式与顶点缓冲区绑定混为一谈。 glVertexAttrib
将两者结合在一个调用中,但您似乎只有一个一致的顶点格式,该格式由两个绑定组成,一个具有 3 个属性,第二个具有 2 个属性。
查看一些关于 OpenGL 中单独顶点格式的教程,并尝试重构您的 GL 后端以使用它。等效的 Vulkan 管道设置应该变得更加明显。
与 Ratchet 的 GL 调用对应的 Vulkan 顶点绑定和属性描述应该如下所示
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) },
};
扩展 JHerico 的回答。
如果您使用来自 opengl 的单独顶点属性,您会得到这种形式:
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);
然后在渲染时将绑定 1 的偏移量设置为第 1 帧的偏移量,将绑定 2 的偏移量设置为第 2 帧的偏移量。
void SetupVertexBufferFormat(unsigned int frame1, unsigned int frame2) {
glBindVertexBuffer(1, vbo, &vNull[frame1], sizeof(Vertex));
glBindVertexBuffer(2, vbo, &vNull[frame2], sizeof(Vertex));
}
这可以直接转化为 vulkan 的状态。
我目前的任务是将 OpenGL 代码库转换为 Vulkan,但现在无意中发现了此代码如何使用顶点缓冲区的问题,因为它非常动态地改变布局以将其作为大量帧来处理。
为了渲染它的动画模型,它设置了一个包含整个模型所有数据的大缓冲区,并调用以下函数来设置它:
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);
}
这在 OpenGL 上很好,但在 Vulkan 上,顶点缓冲区布局是管道对象的一部分!这意味着移植设置需要每帧创建和销毁多个管道,因为 frame1 和 frame2 值可能几乎是随机组合的。
不能做的是改变缓冲区的内容,这是由前端生成的,这是禁止的,因为它仍然需要与现有的 OpenGL 后端一起工作。
是否有任何解决方法,或者一些复杂的管道管理是这里唯一的选择吗?
您似乎将顶点格式与顶点缓冲区绑定混为一谈。 glVertexAttrib
将两者结合在一个调用中,但您似乎只有一个一致的顶点格式,该格式由两个绑定组成,一个具有 3 个属性,第二个具有 2 个属性。
查看一些关于 OpenGL 中单独顶点格式的教程,并尝试重构您的 GL 后端以使用它。等效的 Vulkan 管道设置应该变得更加明显。
与 Ratchet 的 GL 调用对应的 Vulkan 顶点绑定和属性描述应该如下所示
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) },
};
扩展 JHerico 的回答。
如果您使用来自 opengl 的单独顶点属性,您会得到这种形式:
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);
然后在渲染时将绑定 1 的偏移量设置为第 1 帧的偏移量,将绑定 2 的偏移量设置为第 2 帧的偏移量。
void SetupVertexBufferFormat(unsigned int frame1, unsigned int frame2) {
glBindVertexBuffer(1, vbo, &vNull[frame1], sizeof(Vertex));
glBindVertexBuffer(2, vbo, &vNull[frame2], sizeof(Vertex));
}
这可以直接转化为 vulkan 的状态。