Code cleanup and readme for vertex attributes sample

This commit is contained in:
Sascha Willems 2022-01-21 13:35:06 +01:00
parent e34634c266
commit 6b0bc52a1b
4 changed files with 77 additions and 19 deletions

View file

@ -2,4 +2,60 @@
## Synopsis ## Synopsis
This sample demonstrates how to pass vertex attributes using interleaved or separate buffers. This sample demonstrates two different ways of providing vertex data to the GPU using either interleaved or separate buffers for vertex attributes.
## Shader interface
The shader interface for passing the vertex attributes is the same, no matter if the data provided is coming from a single interleaved or multiple separate buffers.
```glsl
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec2 inUV;
layout (location = 3) in vec4 inTangent;
```
## Interleaved vertex attributes
In an interleaved vertex buffer, the components that make up a single vertex are stored after each other, so the stride of a single vertex is the sum of it's component's sizes.
![Interleaved buffer layout](interleavedbuffer.png)
```cpp
// Binding
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsInterleaved = {
{ 0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX },
};
// Attribute
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesInterleaved = {
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos) },
{ 1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal) },
{ 2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv) },
{ 3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, tangent) },
};
```
## Separate vertex attributes
When using separate buffers, each component is stored in it's own buffer. So e.g. the position buffer only contains vertex positions stored consecutively.
![Interleaved buffer layout](separatebuffers.png)
```cpp
// Bindings
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsSeparate = {
{ 0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX },
{ 1, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX },
{ 2, sizeof(glm::vec2), VK_VERTEX_INPUT_RATE_VERTEX },
{ 3, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX },
};
// Attributes
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesSeparate = {
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 },
{ 1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0 },
{ 2, 2, VK_FORMAT_R32G32_SFLOAT, 0 },
{ 3, 3, VK_FORMAT_R32G32B32A32_SFLOAT, 0 },
};
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -90,7 +90,7 @@ void VulkanExample::loadSceneNode(const tinygltf::Node& inputNode, const tinyglt
vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f))); vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f)));
vert.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f); vert.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f);
vert.tangent = tangentsBuffer ? glm::make_vec4(&tangentsBuffer[v * 4]) : glm::vec4(0.0f); vert.tangent = tangentsBuffer ? glm::make_vec4(&tangentsBuffer[v * 4]) : glm::vec4(0.0f);
vertexBuffer.push_back(vert); vertexBuffer.push_back(vert);
// Append separate attributes // Append separate attributes
vertexAttributes.pos.push_back(glm::make_vec3(&positionBuffer[v * 3])); vertexAttributes.pos.push_back(glm::make_vec3(&positionBuffer[v * 3]));
@ -230,7 +230,8 @@ void VulkanExample::buildCommandBuffers()
VkDeviceSize offsets[4] = { 0, 0, 0, 0 }; VkDeviceSize offsets[4] = { 0, 0, 0, 0 };
std::array<VkBuffer, 4> buffers = { separateVertexBuffers.pos.buffer, separateVertexBuffers.normal.buffer, separateVertexBuffers.uv.buffer, separateVertexBuffers.tangent.buffer }; std::array<VkBuffer, 4> buffers = { separateVertexBuffers.pos.buffer, separateVertexBuffers.normal.buffer, separateVertexBuffers.uv.buffer, separateVertexBuffers.tangent.buffer };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, static_cast<uint32_t>(buffers.size()), buffers.data(), offsets); vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, static_cast<uint32_t>(buffers.size()), buffers.data(), offsets);
} else { }
else {
// Using interleaved attribute bindings only requires one buffer to be bound // Using interleaved attribute bindings only requires one buffer to be bound
VkDeviceSize offsets[1] = { 0 }; VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &interleavedVertexBuffer.buffer, offsets); vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &interleavedVertexBuffer.buffer, offsets);
@ -492,13 +493,13 @@ void VulkanExample::preparePipelines()
// Interleaved vertex attributes // Interleaved vertex attributes
// One Binding (one buffer) and multiple attributes // One Binding (one buffer) and multiple attributes
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsInterleaved = { const std::vector<VkVertexInputBindingDescription> vertexInputBindingsInterleaved = {
vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX), { 0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX },
}; };
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesInterleaved = { const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesInterleaved = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)), { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos) },
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)), { 1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal) },
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)), { 2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv) },
vks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, tangent)), { 3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, tangent) },
}; };
vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(vertexInputBindingsInterleaved, vertexInputAttributesInterleaved); vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(vertexInputBindingsInterleaved, vertexInputAttributesInterleaved);
@ -507,17 +508,18 @@ void VulkanExample::preparePipelines()
// Separate vertex attribute // Separate vertex attribute
// Multiple bindings (for each attribute buffer) and multiple attribues // Multiple bindings (for each attribute buffer) and multiple attribues
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsSeparate = { const std::vector<VkVertexInputBindingDescription> vertexInputBindingsSeparate = {
vks::initializers::vertexInputBindingDescription(0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX), { 0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX },
vks::initializers::vertexInputBindingDescription(1, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX), { 1, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX },
vks::initializers::vertexInputBindingDescription(2, sizeof(glm::vec2), VK_VERTEX_INPUT_RATE_VERTEX), { 2, sizeof(glm::vec2), VK_VERTEX_INPUT_RATE_VERTEX },
vks::initializers::vertexInputBindingDescription(3, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX), { 3, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX },
}; };
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesSeparate = { const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesSeparate = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 },
vks::initializers::vertexInputAttributeDescription(1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0), { 1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0 },
vks::initializers::vertexInputAttributeDescription(2, 2, VK_FORMAT_R32G32_SFLOAT, 0), { 2, 2, VK_FORMAT_R32G32_SFLOAT, 0 },
vks::initializers::vertexInputAttributeDescription(3, 3, VK_FORMAT_R32G32B32A32_SFLOAT, 0), { 3, 3, VK_FORMAT_R32G32B32A32_SFLOAT, 0 },
}; };
vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(vertexInputBindingsSeparate, vertexInputAttributesSeparate); vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(vertexInputBindingsSeparate, vertexInputAttributesSeparate);
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.vertexAttributesSeparate)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.vertexAttributesSeparate));
} }