提问人:gmmo 提问时间:11/9/2023 最后编辑:Rabbid76gmmo 更新时间:11/10/2023 访问量:47
Vulkan VkVertexInputBindingDescription,它是如何工作的?
Vulkan VkVertexInputBindingDescription, how does it work?
问:
我正在尝试做一些我认为很简单的事情,但我无法让它工作。我对 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.
如果竞价是顶点缓冲区的索引,那么这就是我尝试执行的操作:我想使用不同的位置将两个不同的缓冲区绑定到我的顶点着色器。我有理由这样做,但与这篇文章无关。这是我尝试过的:
- 创建了我的结构和顶点缓冲区:请注意,我希望一个缓冲区包含所有位置,另一个缓冲区包含所有颜色
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
};
- 下面是我的属性和绑定描述数组。不要担心步幅,我正在使用它,它允许在命令缓冲区创建期间指定步幅。
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;
}
- 我创建顶点缓冲区(一个用于位置,一个用于颜色)
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;
- 我是这样设置的:
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();
在这一点上,事情似乎正在奏效。创建管道时,我没有看到验证层的错误。
- 这是顶点着色器
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;
}
- 最后,这是我如何创建命令缓冲区。
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)
附件是调试器的屏幕截图,以供参考: 对象和数组对我来说看起来不错。
我认为从根本上说,我仍然不明白绑定的作用。它是顶点缓冲区的索引,还是其他东西?这是什么?
非常感谢您的耐心阅读。我非常感谢任何帮助或解释为什么这不起作用以及我做错了什么。
答:
它们是一回事,通过 VkVertexInputBindingDescription 定义要使用的缓冲区,在创建属性时,定义与要使用的缓冲区的绑定。
您得到的错误是因为当您调用 vkCmdBindVertexBuffers2 时,您告诉它仅绑定第一个缓冲区,将 1 替换为 2。
我觉得很奇怪,您正在使用 vkCmdBindVertexBuffers2 并且没有指定创建 VkVertexInputBindingDescriptions 时的步幅,这是有原因的吗?此外,在这种情况下,我建议使用一个顶点缓冲区,但您确实说您“有理由这样做”,所以我就不说了。
评论