Code restructuring

This commit is contained in:
Sascha Willems 2022-01-21 09:59:30 +01:00
parent 15124e8232
commit 0dc34c3375
2 changed files with 73 additions and 82 deletions

View file

@ -153,7 +153,7 @@ void VulkanExample::loadSceneNode(const tinygltf::Node& inputNode, const tinyglt
VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{ {
title = "Separate vertex attribute buffers"; title = "Separate/interleaved vertex attribute buffers";
camera.type = Camera::CameraType::firstperson; camera.type = Camera::CameraType::firstperson;
camera.flipY = true; camera.flipY = true;
camera.setPosition(glm::vec3(0.0f, 1.0f, 0.0f)); camera.setPosition(glm::vec3(0.0f, 1.0f, 0.0f));
@ -170,10 +170,10 @@ VulkanExample::~VulkanExample()
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr);
indices.destroy(); indices.destroy();
shaderData.buffer.destroy(); shaderData.buffer.destroy();
vertexAttibuteBuffers.normal.destroy(); separateVertexBuffers.normal.destroy();
vertexAttibuteBuffers.pos.destroy(); separateVertexBuffers.pos.destroy();
vertexAttibuteBuffers.tangent.destroy(); separateVertexBuffers.tangent.destroy();
vertexAttibuteBuffers.uv.destroy(); separateVertexBuffers.uv.destroy();
for (Image image : scene.images) { for (Image image : scene.images) {
vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr); vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr);
vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr); vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr);
@ -228,7 +228,7 @@ void VulkanExample::buildCommandBuffers()
if (vertexAttributeSettings == VertexAttributeSettings::separate) { if (vertexAttributeSettings == VertexAttributeSettings::separate) {
// Using separate vertex attribute bindings requires binding multiple attribute buffers // Using separate vertex attribute bindings requires binding multiple attribute buffers
VkDeviceSize offsets[4] = { 0, 0, 0, 0 }; VkDeviceSize offsets[4] = { 0, 0, 0, 0 };
std::array<VkBuffer, 4> buffers = { vertexAttibuteBuffers.pos.buffer, vertexAttibuteBuffers.normal.buffer, vertexAttibuteBuffers.uv.buffer, vertexAttibuteBuffers.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
@ -316,94 +316,84 @@ void VulkanExample::uploadVertexData()
// Upload vertex and index buffers // Upload vertex and index buffers
// Anonymous functions to simplify buffer creation // Anonymous functions to simplify buffer creation
// Create a staging buffer used as a source for copies // Create a staging buffer used as a source for copies
auto createStagingBuffer = [this](vks::Buffer& buffer, void* data, VkDeviceSize size) { auto createStagingBuffer = [this](vks::Buffer& buffer, void* data, VkDeviceSize size) {
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &buffer, size, data)); VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &buffer, size, data));
}; };
// Create a device local buffer used as a target for copies // Create a device local buffer used as a target for copies
auto createDeviceBuffer = [this](vks::Buffer& buffer, VkDeviceSize size, VkBufferUsageFlags usageFlags = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) { auto createDeviceBuffer = [this](vks::Buffer& buffer, VkDeviceSize size, VkBufferUsageFlags usageFlags = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) {
VK_CHECK_RESULT(vulkanDevice->createBuffer(usageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &buffer, size)); VK_CHECK_RESULT(vulkanDevice->createBuffer(usageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &buffer, size));
}; };
size_t vertexBufferSize = vertexBuffer.size() * sizeof(Vertex); VkCommandBuffer copyCmd;
size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t); VkBufferCopy copyRegion{};
vks::Buffer vertexStaging, indexStaging;
createStagingBuffer(indexStaging, indexBuffer.data(), indexBufferSize);
createStagingBuffer(vertexStaging, vertexBuffer.data(), vertexBufferSize);
createDeviceBuffer(indices, indexStaging.size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
createDeviceBuffer(interleavedVertexBuffer, vertexStaging.size);
// Copy data from staging buffers (host) do device local buffer (gpu)
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
VkBufferCopy copyRegion = {};
copyRegion.size = vertexBufferSize;
vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, interleavedVertexBuffer.buffer, 1, &copyRegion);
copyRegion.size = indexBufferSize;
vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, &copyRegion);
vulkanDevice->flushCommandBuffer(copyCmd, queue, true);
// Free staging resources
vkDestroyBuffer(device, vertexStaging.buffer, nullptr);
vkFreeMemory(device, vertexStaging.memory, nullptr);
vkDestroyBuffer(device, indexStaging.buffer, nullptr);
vkFreeMemory(device, indexStaging.memory, nullptr);
/* /*
Interleaved vertex attributes Interleaved vertex attributes
We create one single buffer containing the interleaved vertex attributes We create one single buffer containing the interleaved vertex attributes
*/ */
size_t vertexBufferSize = vertexBuffer.size() * sizeof(Vertex);
vks::Buffer vertexStaging;
createStagingBuffer(vertexStaging, vertexBuffer.data(), vertexBufferSize);
createDeviceBuffer(interleavedVertexBuffer, vertexStaging.size);
// Copy data from staging buffer (host) do device local buffer (gpu)
copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
copyRegion.size = vertexBufferSize;
vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, interleavedVertexBuffer.buffer, 1, &copyRegion);
vulkanDevice->flushCommandBuffer(copyCmd, queue, true);
vertexStaging.destroy();
/* /*
Separate vertex attributes Separate vertex attributes
We create a separate buffer for each of the vertex attributes (position, normals, etc.) We create multiple separate buffers for each of the vertex attributes (position, normals, etc.)
*/ */
std::array<vks::Buffer, 4> stagingBuffers; std::array<vks::Buffer, 4> stagingBuffers;
createStagingBuffer(stagingBuffers[0], vertexAttributes.pos.data(), vertexAttributes.pos.size() * sizeof(vertexAttributes.pos[0])); createStagingBuffer(stagingBuffers[0], vertexAttributes.pos.data(), vertexAttributes.pos.size() * sizeof(vertexAttributes.pos[0]));
createStagingBuffer(stagingBuffers[1], vertexAttributes.normal.data(), vertexAttributes.normal.size() * sizeof(vertexAttributes.normal[0])); createStagingBuffer(stagingBuffers[1], vertexAttributes.normal.data(), vertexAttributes.normal.size() * sizeof(vertexAttributes.normal[0]));
createStagingBuffer(stagingBuffers[2], vertexAttributes.uv.data(), vertexAttributes.uv.size() * sizeof(vertexAttributes.uv[0])); createStagingBuffer(stagingBuffers[2], vertexAttributes.uv.data(), vertexAttributes.uv.size() * sizeof(vertexAttributes.uv[0]));
createStagingBuffer(stagingBuffers[3], vertexAttributes.tangent.data(), vertexAttributes.tangent.size() * sizeof(vertexAttributes.tangent[0])); createStagingBuffer(stagingBuffers[3], vertexAttributes.tangent.data(), vertexAttributes.tangent.size() * sizeof(vertexAttributes.tangent[0]));
createDeviceBuffer(vertexAttibuteBuffers.pos, stagingBuffers[0].size); createDeviceBuffer(separateVertexBuffers.pos, stagingBuffers[0].size);
createDeviceBuffer(vertexAttibuteBuffers.normal, stagingBuffers[1].size); createDeviceBuffer(separateVertexBuffers.normal, stagingBuffers[1].size);
createDeviceBuffer(vertexAttibuteBuffers.uv, stagingBuffers[2].size); createDeviceBuffer(separateVertexBuffers.uv, stagingBuffers[2].size);
createDeviceBuffer(vertexAttibuteBuffers.tangent, stagingBuffers[3].size); createDeviceBuffer(separateVertexBuffers.tangent, stagingBuffers[3].size);
// Stage // Stage
std::vector<vks::Buffer> attributeBuffers = { std::vector<vks::Buffer> attributeBuffers = {
vertexAttibuteBuffers.pos, separateVertexBuffers.pos,
vertexAttibuteBuffers.normal, separateVertexBuffers.normal,
vertexAttibuteBuffers.uv, separateVertexBuffers.uv,
vertexAttibuteBuffers.tangent, separateVertexBuffers.tangent,
}; };
// Copy data from staging buffers (host) do device local buffer (gpu) // Copy data from staging buffer (host) do device local buffer (gpu)
copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
copyRegion = {};
for (size_t i = 0; i < attributeBuffers.size(); i++) { for (size_t i = 0; i < attributeBuffers.size(); i++) {
copyRegion.size = attributeBuffers[i].size; copyRegion.size = attributeBuffers[i].size;
vkCmdCopyBuffer(copyCmd, stagingBuffers[i].buffer, attributeBuffers[i].buffer, 1, &copyRegion); vkCmdCopyBuffer(copyCmd, stagingBuffers[i].buffer, attributeBuffers[i].buffer, 1, &copyRegion);
} }
vulkanDevice->flushCommandBuffer(copyCmd, queue, true); vulkanDevice->flushCommandBuffer(copyCmd, queue, true);
for (size_t i = 0; i < 4; i++) {
stagingBuffers[i].destroy();
}
/* /*
Index buffer Index buffer
The index buffer is always the same, no matter how we pass the vertex attributes The index buffer is always the same, no matter how we pass the vertex attributes
*/ */
size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
// @todo: clear vks::Buffer indexStaging;
for (size_t i = 0; i < 4; i++) { createStagingBuffer(indexStaging, indexBuffer.data(), indexBufferSize);
vkDestroyBuffer(device, stagingBuffers[i].buffer, nullptr); createDeviceBuffer(indices, indexStaging.size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
vkFreeMemory(device, stagingBuffers[i].memory, nullptr); // Copy data from staging buffer (host) do device local buffer (gpu)
} copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
copyRegion.size = indexBufferSize;
vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, &copyRegion);
vulkanDevice->flushCommandBuffer(copyCmd, queue, true);
// Free staging resources
indexStaging.destroy();
} }
void VulkanExample::loadAssets() void VulkanExample::loadAssets()
@ -484,31 +474,6 @@ void VulkanExample::preparePipelines()
VkPipelineVertexInputStateCreateInfo vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(); VkPipelineVertexInputStateCreateInfo vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo();
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages; std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
// @todo: comment
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsInterleaved = {
vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX),
};
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesInterleaved = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)),
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)),
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, uv)),
vks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, tangent)),
};
// @todo: comment
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsSeparate = {
vks::initializers::vertexInputBindingDescription(0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX),
vks::initializers::vertexInputBindingDescription(1, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX),
vks::initializers::vertexInputBindingDescription(2, sizeof(glm::vec2), VK_VERTEX_INPUT_RATE_VERTEX),
vks::initializers::vertexInputBindingDescription(3, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX),
};
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesSeparate = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0),
vks::initializers::vertexInputAttributeDescription(1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0),
vks::initializers::vertexInputAttributeDescription(2, 2, VK_FORMAT_R32G32B32_SFLOAT, 0),
vks::initializers::vertexInputAttributeDescription(3, 3, VK_FORMAT_R32G32B32_SFLOAT, 0),
};
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
pipelineCI.pVertexInputState = &vertexInputStateCI; pipelineCI.pVertexInputState = &vertexInputStateCI;
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
@ -524,9 +489,35 @@ void VulkanExample::preparePipelines()
shaderStages[0] = loadShader(getShadersPath() + "vertexattributes/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getShadersPath() + "vertexattributes/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "vertexattributes/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getShadersPath() + "vertexattributes/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
// Interleaved vertex attributes
// One Binding (one buffer) and multiple attributes
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsInterleaved = {
vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX),
};
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesInterleaved = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)),
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)),
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, uv)),
vks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, tangent)),
};
vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(vertexInputBindingsInterleaved, vertexInputAttributesInterleaved); vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(vertexInputBindingsInterleaved, vertexInputAttributesInterleaved);
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.vertexAttributesInterleaved)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.vertexAttributesInterleaved));
// Separate vertex attribute
// Multiple bindings (for each attribute buffer) and multiple attribues
const std::vector<VkVertexInputBindingDescription> vertexInputBindingsSeparate = {
vks::initializers::vertexInputBindingDescription(0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX),
vks::initializers::vertexInputBindingDescription(1, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX),
vks::initializers::vertexInputBindingDescription(2, sizeof(glm::vec2), VK_VERTEX_INPUT_RATE_VERTEX),
vks::initializers::vertexInputBindingDescription(3, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX),
};
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributesSeparate = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0),
vks::initializers::vertexInputAttributeDescription(1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0),
vks::initializers::vertexInputAttributeDescription(2, 2, VK_FORMAT_R32G32B32_SFLOAT, 0),
vks::initializers::vertexInputAttributeDescription(3, 3, VK_FORMAT_R32G32B32_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));
} }

View file

@ -87,9 +87,9 @@ public:
// Buffers for the separate vertex attributes // Buffers for the separate vertex attributes
// @todo: rename // @todo: rename
struct VertexAttributeBuffers { struct SeparateVertexBuffers {
vks::Buffer pos, normal, uv, tangent; vks::Buffer pos, normal, uv, tangent;
} vertexAttibuteBuffers; } separateVertexBuffers;
// Single vertex buffer for all primitives // Single vertex buffer for all primitives
vks::Buffer interleavedVertexBuffer; vks::Buffer interleavedVertexBuffer;