Vulkan VkVertexInputBindingDescription,它是如何工作的?

Vulkan VkVertexInputBindingDescription, how does it work?

提问人:gmmo 提问时间:11/9/2023 最后编辑:Rabbid76gmmo 更新时间:11/10/2023 访问量:47

问:

我正在尝试做一些我认为很简单的事情,但我无法让它工作。我对 Vulkan 绑定几乎没有问题。

问题1.VkVertexInputBindingDescription.binding 和 VkVertexInputAttributeDescription.binding 有什么区别?

根据规范,差异似乎非常微妙,我不太明白。

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkVertexInputBindingDescription.html https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkVertexInputAttributeDescription.html

VkVertexInputBindingDescription.binding = binding is the binding number that this structure describes.
VkVertexInputAttributeDescription.binding = is the binding number which this attribute takes its data from.

问题2.是一回事吗?根据这篇文章,这更容易理解

“VkVertexInputBindingDescription”中的“binding”的目的是什么?

binding is an index into the pBuffers array bound by vkCmdBindVertexBuffers.

如果竞价是顶点缓冲区的索引,那么这就是我尝试执行的操作:我想使用不同的位置将两个不同的缓冲区绑定到我的顶点着色器。我有理由这样做,但与这篇文章无关。这是我尝试过的:

  1. 创建了我的结构和顶点缓冲区:请注意,我希望一个缓冲区包含所有位置,另一个缓冲区包含所有颜色
struct Vertex {glm::vec2 pos;};
struct Color {glm::vec3 color;};
const std::vector<Vertex> verts = {
    {{-1.0f, -1.0f}}, {{0.0f, -1.0f}}, {{-1.0f, 0.0f}}, // tri0 pos
    {{-1.0f, 0.0f}},  {{0.0f, -1.0f}}, {{0.0f, 0.0f}},  // tri1 pos
};
const std::vector<Color> colors = {
    {{1.0f, 1.0f, 0.0f}}, {{1.0f, 1.0f, 0.0f}}, {{1.0f, 1.0f, 0.0f}}, //tri0 color
    {{0.0f, 0.0f, 1.0f}}, {{0.0f, 0.0f, 1.0f}}, {{0.0f, 0.0f, 1.0f}}, //tri1 color
};
  1. 下面是我的属性和绑定描述数组。不要担心步幅,我正在使用它,它允许在命令缓冲区创建期间指定步幅。vkCmdBindVertexBuffers2
static std::array <VkVertexInputBindingDescription,2> getBindingDescription() {

    std::array <VkVertexInputBindingDescription, 2> bindingDescription{};
    bindingDescription[0].binding = 0; // index to Vertex buffer?
    bindingDescription[0].stride = 99; //dummy
    bindingDescription[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

    bindingDescription[1].binding = 1;
    bindingDescription[1].stride = 99; //dummy
    bindingDescription[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

    return bindingDescription;
}

static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() {
    std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{};

    attributeDescriptions[0].binding = 0;   // index to Vertex buffer?
    attributeDescriptions[0].location = 0;
    attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
    attributeDescriptions[0].offset = offsetof(Vertex, pos);

    attributeDescriptions[1].binding = 1;
    attributeDescriptions[1].location = 1;
    attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
    attributeDescriptions[1].offset = offsetof(Color, color);

    return attributeDescriptions;
}
  1. 我创建顶点缓冲区(一个用于位置,一个用于颜色)
VkBuffer vertexBuffer[2];
float* p1 = (float*)verts.data(); auto s1 = verts.size() * sizeof(Vertex);
float* p2 = (float*)colors.data(); auto s2 = colors.size() * sizeof(Color);
vertexBuffer[0]= createVertexBuffer(p1, s1);
vertexBuffer[1] = createVertexBuffer(p2, s2);

// a bunch of code
VkBuffer createVertexBuffer(float* pVertices, int count) {
    VkBufferCreateInfo bufferInfo{};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = sizeof(float) * count;
    bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
     bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
     // a bunch of code
return vertexBuffer;

  1. 我是这样设置的:VkPipelineVertexInputStateCreateInfo
        VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
        auto bindingDescription = getBindingDescription();
        auto attributeDescriptions = getAttributeDescriptions();
        vertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(bindingDescription.size());;
        vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
        vertexInputInfo.pVertexBindingDescriptions = bindingDescription.data();
        vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

在这一点上,事情似乎正在奏效。创建管道时,我没有看到验证层的错误。

  1. 这是顶点着色器
layout(location = 0) in vec2 inPosition; // I want to bind vertexBuffer[0] here
layout(location = 1) in vec3 inColor;    // I want to bind vertexBuffer[1] here
layout(location = 0) out vec3 fragColor;
void main() {
    gl_Position = vec4(inPosition, 0.0, 1.0);
    fragColor = inColor;
}
  1. 最后,这是我如何创建命令缓冲区。
VkBuffer vertexBuffers[] = { vertexBuffer[0], vertexBuffer[1] };
VkDeviceSize offsets[] = { 0, 0 };
auto numVertices = static_cast<uint32_t>(verts.size());
auto numColors = static_cast<uint32_t>(colors.size());
VkDeviceSize    sizes[] = { sizeof(Vertex) * numVertices, sizeof(Color) * numVertices };
VkDeviceSize    strides[] = { sizeof(Vertex), sizeof(Color) };
vkCmdBindVertexBuffers2(commandBuffer, 0, 1, vertexBuffers, offsets, sizes, strides);

// this fails :(
vkCmdDraw(commandBuffer, numVertices, 1, 0, 0);

它绑定没有错误,但是当我发出绘制调用时,验证层报告了一堆错误,如下所示(还有更多错误,但试图保持帖子的可读性),并且没有绘制任何内容。

VUID-vkCmdDraw-None-04007(ERROR / SPEC): msgNum: -1719549157 - Validation Error: [ VUID-vkCmdDraw-None-04007 ] Object 0: handle = 0x254738c3e80, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x9981c31b | vkCmdDraw: VkPipeline 0x967dd1000000000e[] expects that this Command Buffer's vertex binding Index 1 should be set via vkCmdBindVertexBuffers. This is because pVertexBindingDescriptions[1].binding value is 1. The Vulkan spec states: All vertex input bindings accessed via vertex input variables declared in the vertex shader entry point's interface must have either valid or VK_NULL_HANDLE buffers bound (https://vulkan.lunarg.com/doc/view/1.3.236.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdDraw-None-04007)
    Objects: 1
        [0] 0x254738c3e80, type: 6, name: NULL
validation layer: Validation Error: [ VUID-vkCmdDraw-None-04007 ] Object 0: handle = 0x254738c3e80, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x9981c31b | vkCmdDraw: VkPipeline 0x967dd1000000000e[] expects that this Command Buffer's vertex binding Index 1 should be set via vkCmdBindVertexBuffers. This is because pVertexBindingDescriptions[1].binding value is 1. The Vulkan spec states: All vertex input bindings accessed via vertex input variables declared in the vertex shader entry point's interface must have either valid or VK_NULL_HANDLE buffers bound (https://vulkan.lunarg.com/doc/view/1.3.236.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdDraw-None-04007)
VUID-vkCmdDraw-None-02721(ERROR / SPEC): msgNum: -1712364613 - Validation Error: [ VUID-vkCmdDraw-None-02721 ] Object 0: handle = 0x254738c3e80, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x967dd1000000000e, type = VK_OBJECT_TYPE_PIPELINE; | MessageID = 0x99ef63bb | vkCmdDraw: binding #1 in pVertexAttributeDescriptions[1] of VkPipeline 0x967dd1000000000e[] is an invalid value for command buffer VkCommandBuffer 0x254738c3e80[]. The Vulkan spec states: For a given vertex buffer binding, any attribute data fetched must be entirely contained within the corresponding vertex buffer binding, as described in Vertex Input Description (https://vulkan.lunarg.com/doc/view/1.3.236.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdDraw-None-02721)

附件是调试器的屏幕截图,以供参考: 对象和数组对我来说看起来不错。

我认为从根本上说,我仍然不明白绑定的作用。它是顶点缓冲区的索引,还是其他东西?这是什么?

非常感谢您的耐心阅读。我非常感谢任何帮助或解释为什么这不起作用以及我做错了什么。

enter image description here

GLSL Vulkan

评论

0赞 Rabbid76 11/9/2023
请不要上传代码/数据/错误的图像。

答:

3赞 Zachary Peterson 11/10/2023 #1

它们是一回事,通过 VkVertexInputBindingDescription 定义要使用的缓冲区,在创建属性时,定义与要使用的缓冲区的绑定。

您得到的错误是因为当您调用 vkCmdBindVertexBuffers2 时,您告诉它仅绑定第一个缓冲区,将 1 替换为 2。

我觉得很奇怪,您正在使用 vkCmdBindVertexBuffers2 并且没有指定创建 VkVertexInputBindingDescriptions 时的步幅,这是有原因的吗?此外,在这种情况下,我建议使用一个顶点缓冲区,但您确实说您“有理由这样做”,所以我就不说了。

评论

0赞 gmmo 11/10/2023
它现在起作用了!感谢!