Move drawing into dedicated VulkanglTF class

Comments, code-cleanup
This commit is contained in:
Sascha Willems 2020-04-12 22:07:54 +02:00
parent 9fa9a4b46b
commit 2966d0ee5d

View file

@ -41,13 +41,13 @@
#define ENABLE_VALIDATION false #define ENABLE_VALIDATION false
// Contains everything required to render a glTF model in Vulkan // Contains everything required to render a glTF model in Vulkan
// This class is very simplified but retains the basic glTF structure // This class is heavily simplified (compared to glTF's feature set) but retains the basic glTF structure
class VulkanglTFModel class VulkanglTFModel
{ {
public: public:
// The class requires some Vulkan objects so it can create it's own resources // The class requires some Vulkan objects so it can create it's own resources
vks::VulkanDevice* vulkanDevice; vks::VulkanDevice* vulkanDevice;
VkQueue copyQueue; VkQueue copyQueue;
// The vertex layout for the samples' model // The vertex layout for the samples' model
struct Vertex { struct Vertex {
@ -321,6 +321,40 @@ public:
} }
} }
/*
glTF rendering functions
*/
// Draw a single node including child nodes (if present)
void drawglTFNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node node)
{
if (node.mesh.primitives.size() > 0) {
for (VulkanglTFModel::Primitive& primitive : node.mesh.primitives) {
if (primitive.indexCount > 0) {
// Get the texture index for this primitive
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
}
}
}
for (auto& child : node.children) {
drawglTFNode(commandBuffer, pipelineLayout, child);
}
}
// Draw the glTF scene starting at the top-level-nodes
void draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout)
{
// All vertices and indices are stored in single buffers, so we only need to bind once
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
// Render all nodes at top-level
for (auto& node : nodes) {
drawglTFNode(commandBuffer, pipelineLayout, node);
}
}
}; };
@ -423,44 +457,13 @@ public:
// Bind scene matrices descriptor to set 0 // Bind scene matrices descriptor to set 0
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid);
drawglTFModel(drawCmdBuffers[i]); glTFModel.draw(drawCmdBuffers[i], pipelineLayout);
drawUI(drawCmdBuffers[i]); drawUI(drawCmdBuffers[i]);
vkCmdEndRenderPass(drawCmdBuffers[i]); vkCmdEndRenderPass(drawCmdBuffers[i]);
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
} }
} }
/*
glTF rendering functions
*/
void drawglTFNode(VkCommandBuffer commandBuffer, VulkanglTFModel::Node node)
{
if (node.mesh.primitives.size() > 0) {
for (VulkanglTFModel::Primitive& primitive : node.mesh.primitives) {
if (primitive.indexCount > 0) {
// @todo: link mat to node
VulkanglTFModel::Texture texture = glTFModel.textures[glTFModel.materials[primitive.materialIndex].baseColorTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &glTFModel.images[texture.imageIndex].descriptorSet, 0, nullptr);
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
}
}
}
for (auto& child : node.children) {
drawglTFNode(commandBuffer, child);
}
}
void drawglTFModel(VkCommandBuffer commandBuffer)
{
// All vertices and indices are stored in single buffers, so we only need to bind once
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &glTFModel.vertices.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, glTFModel.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
for (auto& node : glTFModel.nodes) {
drawglTFNode(commandBuffer, node);
}
}
// @todo // @todo
void loadglTF(std::string filename) void loadglTF(std::string filename)
{ {
@ -486,6 +489,7 @@ public:
bool fileLoaded = gltfContext.LoadASCIIFromFile(&glTFInput, &error, &warning, filename); bool fileLoaded = gltfContext.LoadASCIIFromFile(&glTFInput, &error, &warning, filename);
#endif #endif
// Pass some Vulkan resources required for setup and rendering to the glTF model loading class
glTFModel.vulkanDevice = vulkanDevice; glTFModel.vulkanDevice = vulkanDevice;
glTFModel.copyQueue = queue; glTFModel.copyQueue = queue;
@ -508,6 +512,10 @@ public:
return; return;
} }
// Create and upload vertex and index buffer
// We will be using one single vertex buffer and one single index buffer for the whole glTF scene
// Primitives (of the glTF model) will then index into these using index offsets
size_t vertexBufferSize = vertexBuffer.size() * sizeof(VulkanglTFModel::Vertex); size_t vertexBufferSize = vertexBuffer.size() * sizeof(VulkanglTFModel::Vertex);
size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t); size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
glTFModel.indices.count = static_cast<uint32_t>(indexBuffer.size()); glTFModel.indices.count = static_cast<uint32_t>(indexBuffer.size());
@ -517,8 +525,7 @@ public:
VkDeviceMemory memory; VkDeviceMemory memory;
} vertexStaging, indexStaging; } vertexStaging, indexStaging;
// Create staging buffers // Create host visible staging buffers (source)
// Vertex data
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
@ -535,15 +542,13 @@ public:
&indexStaging.memory, &indexStaging.memory,
indexBuffer.data())); indexBuffer.data()));
// Create device local buffers // Create device local buffers (targat)
// Vertex buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
vertexBufferSize, vertexBufferSize,
&glTFModel.vertices.buffer, &glTFModel.vertices.buffer,
&glTFModel.vertices.memory)); &glTFModel.vertices.memory));
// Index buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
@ -553,7 +558,6 @@ public:
// Copy data from staging buffers (host) do device local buffer (gpu) // Copy data from staging buffers (host) do device local buffer (gpu)
VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
VkBufferCopy copyRegion = {}; VkBufferCopy copyRegion = {};
copyRegion.size = vertexBufferSize; copyRegion.size = vertexBufferSize;
@ -574,6 +578,7 @@ public:
VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true);
// Free staging resources
vkDestroyBuffer(device, vertexStaging.buffer, nullptr); vkDestroyBuffer(device, vertexStaging.buffer, nullptr);
vkFreeMemory(device, vertexStaging.memory, nullptr); vkFreeMemory(device, vertexStaging.memory, nullptr);
vkDestroyBuffer(device, indexStaging.buffer, nullptr); vkDestroyBuffer(device, indexStaging.buffer, nullptr);