Global scene memory allocation instead of per-mesh, use VulkanDevice, compiler warnings

This commit is contained in:
saschawillems 2016-07-23 22:47:58 +02:00
parent e4fb1e6126
commit 053d6423fa

View file

@ -1,9 +1,24 @@
/* /*
* Vulkan Example - Rendering a scene with multiple meshes and materials * Vulkan Example - Scene rendering
* *
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*
* Summary:
* Renders a scene made of multiple meshes with different materials and textures.
*
* The example loads a scene made up of multiple meshes into one vertex and index buffer to only
* have one (big) memory allocation. In Vulkan it's advised to keep number of memory allocations
* down and try to allocate large blocks of memory at once instead of having many small allocations.
*
* Every mesh has a separate material and multiple descriptor sets (set = x layout qualifier in GLSL)
* are used to bind a uniform buffer with global matrices and the mesh' material's sampler at once.
*
* To demonstrate another way of passing data the example also uses push constants for passing
* material properties.
*
* Note that this example is just one way of rendering a scene made up of multiple meshes iin Vulkan.
*/ */
#include <stdio.h> #include <stdio.h>
@ -63,9 +78,8 @@ struct SceneMaterial
// Stores per-mesh Vulkan resources // Stores per-mesh Vulkan resources
struct SceneMesh struct SceneMesh
{ {
vk::Buffer vertexBuffer; // Index of first index in the scene buffer
vk::Buffer indexBuffer; uint32_t indexBase;
uint32_t indexCount; uint32_t indexCount;
// Pointer to the material used by this mesh // Pointer to the material used by this mesh
@ -89,6 +103,12 @@ private:
VkDescriptorSetLayout scene; VkDescriptorSetLayout scene;
} descriptorSetLayouts; } descriptorSetLayouts;
// We will be using one single index and vertex buffer
// containing vertices and indices for all meshes in the scene
// This allows us to keep memory allocations down
vk::Buffer vertexBuffer;
vk::Buffer indexBuffer;
VkDescriptorSet descriptorSetScene; VkDescriptorSet descriptorSetScene;
vkTools::VulkanTextureLoader *textureLoader; vkTools::VulkanTextureLoader *textureLoader;
@ -211,12 +231,6 @@ private:
VK_CHECK_RESULT(vkAllocateDescriptorSets(vulkanDevice->logicalDevice, &allocInfo, &materials[i].descriptorSet)); VK_CHECK_RESULT(vkAllocateDescriptorSets(vulkanDevice->logicalDevice, &allocInfo, &materials[i].descriptorSet));
VkDescriptorImageInfo texDescriptor =
vkTools::initializers::descriptorImageInfo(
materials[i].diffuse.sampler,
materials[i].diffuse.view,
VK_IMAGE_LAYOUT_GENERAL);
std::vector<VkWriteDescriptorSet> writeDescriptorSets; std::vector<VkWriteDescriptorSet> writeDescriptorSets;
// todo : only use image sampler descriptor set and use one scene ubo for matrices // todo : only use image sampler descriptor set and use one scene ubo for matrices
@ -226,9 +240,9 @@ private:
materials[i].descriptorSet, materials[i].descriptorSet,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
0, 0,
&texDescriptor)); &materials[i].diffuse.descriptor));
vkUpdateDescriptorSets(vulkanDevice->logicalDevice, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); vkUpdateDescriptorSets(vulkanDevice->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
} }
// Scene descriptor set // Scene descriptor set
@ -247,13 +261,16 @@ private:
0, 0,
&uniformBuffer.descriptor)); &uniformBuffer.descriptor));
vkUpdateDescriptorSets(vulkanDevice->logicalDevice, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); vkUpdateDescriptorSets(vulkanDevice->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
} }
// Load all meshes from the scene and generate the Vulkan resources // Load all meshes from the scene and generate the buffers for rendering them
// for rendering them
void loadMeshes(VkCommandBuffer copyCmd) void loadMeshes(VkCommandBuffer copyCmd)
{ {
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
uint32_t indexBase = 0;
meshes.resize(aScene->mNumMeshes); meshes.resize(aScene->mNumMeshes);
for (uint32_t i = 0; i < meshes.size(); i++) for (uint32_t i = 0; i < meshes.size(); i++)
{ {
@ -264,39 +281,42 @@ private:
std::cout << " Faces: " << aMesh->mNumFaces << std::endl; std::cout << " Faces: " << aMesh->mNumFaces << std::endl;
meshes[i].material = &materials[aMesh->mMaterialIndex]; meshes[i].material = &materials[aMesh->mMaterialIndex];
meshes[i].indexBase = indexBase;
meshes[i].indexCount = aMesh->mNumFaces * 3;
// Vertices // Vertices
std::vector<Vertex> vertices;
vertices.resize(aMesh->mNumVertices);
bool hasUV = aMesh->HasTextureCoords(0); bool hasUV = aMesh->HasTextureCoords(0);
bool hasColor = aMesh->HasVertexColors(0); bool hasColor = aMesh->HasVertexColors(0);
bool hasNormals = aMesh->HasNormals(); bool hasNormals = aMesh->HasNormals();
for (uint32_t v = 0; v < aMesh->mNumVertices; v++) for (uint32_t v = 0; v < aMesh->mNumVertices; v++)
{ {
vertices[v].pos = glm::make_vec3(&aMesh->mVertices[v].x); Vertex vertex;
vertices[v].pos.y = -vertices[v].pos.y; vertex.pos = glm::make_vec3(&aMesh->mVertices[v].x);
vertices[v].uv = hasUV ? glm::make_vec2(&aMesh->mTextureCoords[0][v].x) : glm::vec2(0.0f); vertex.pos.y = -vertex.pos.y;
vertices[v].normal = hasNormals ? glm::make_vec3(&aMesh->mNormals[v].x) : glm::vec3(0.0f); vertex.uv = hasUV ? glm::make_vec2(&aMesh->mTextureCoords[0][v].x) : glm::vec2(0.0f);
vertices[v].normal.y = -vertices[v].normal.y; vertex.normal = hasNormals ? glm::make_vec3(&aMesh->mNormals[v].x) : glm::vec3(0.0f);
vertices[v].color = hasColor ? glm::make_vec3(&aMesh->mColors[0][v].r) : glm::vec3(1.0f); vertex.normal.y = -vertex.normal.y;
vertex.color = hasColor ? glm::make_vec3(&aMesh->mColors[0][v].r) : glm::vec3(1.0f);
vertices.push_back(vertex);
} }
// Indices // Indices
std::vector<uint32_t> indices;
meshes[i].indexCount = aMesh->mNumFaces * 3;
indices.resize(aMesh->mNumFaces * 3);
for (uint32_t f = 0; f < aMesh->mNumFaces; f++) for (uint32_t f = 0; f < aMesh->mNumFaces; f++)
{ {
memcpy(&indices[f*3], &aMesh->mFaces[f].mIndices[0], sizeof(uint32_t) * 3); for (uint32_t j = 0; j < 3; j++)
{
indices.push_back(aMesh->mFaces[f].mIndices[j]);
}
}
indexBase += aMesh->mNumFaces * 3;
} }
// Create buffers // Create buffers
// todo : only one memory allocation for the whole scene, use offsets to render // For better performance we only create one index and vertex buffer to keep number of memory allocations down
size_t vertexDataSize = vertices.size() * sizeof(Vertex);
uint32_t vertexDataSize = vertices.size() * sizeof(Vertex); size_t indexDataSize = indices.size() * sizeof(uint32_t);
uint32_t indexDataSize = indices.size() * sizeof(uint32_t);
vk::Buffer vertexStaging, indexStaging; vk::Buffer vertexStaging, indexStaging;
@ -306,28 +326,28 @@ private:
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,
&vertexStaging, &vertexStaging,
vertexDataSize, static_cast<uint32_t>(vertexDataSize),
vertices.data())); vertices.data()));
// Target // Target
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,
&meshes[i].vertexBuffer, &vertexBuffer,
vertexDataSize)); static_cast<uint32_t>(vertexDataSize)));
// Index buffer // Index buffer
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,
&indexStaging, &indexStaging,
indexDataSize, static_cast<uint32_t>(indexDataSize),
indices.data())); indices.data()));
// Target // Target
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,
&meshes[i].indexBuffer, &indexBuffer,
indexDataSize)); static_cast<uint32_t>(indexDataSize)));
// Copy // Copy
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
@ -339,7 +359,7 @@ private:
vkCmdCopyBuffer( vkCmdCopyBuffer(
copyCmd, copyCmd,
vertexStaging.buffer, vertexStaging.buffer,
meshes[i].vertexBuffer.buffer, vertexBuffer.buffer,
1, 1,
&copyRegion); &copyRegion);
@ -347,7 +367,7 @@ private:
vkCmdCopyBuffer( vkCmdCopyBuffer(
copyCmd, copyCmd,
indexStaging.buffer, indexStaging.buffer,
meshes[i].indexBuffer.buffer, indexBuffer.buffer,
1, 1,
&copyRegion); &copyRegion);
@ -365,7 +385,6 @@ private:
vertexStaging.destroy(); vertexStaging.destroy();
indexStaging.destroy(); indexStaging.destroy();
} }
}
public: public:
#if defined(__ANDROID__) #if defined(__ANDROID__)
@ -427,11 +446,8 @@ public:
// Default destructor // Default destructor
~Scene() ~Scene()
{ {
for (auto mesh : meshes) vertexBuffer.destroy();
{ indexBuffer.destroy();
mesh.vertexBuffer.destroy();
mesh.indexBuffer.destroy();
}
for (auto material : materials) for (auto material : materials)
{ {
textureLoader->destroyTexture(material.diffuse); textureLoader->destroyTexture(material.diffuse);
@ -485,16 +501,18 @@ public:
void render(VkCommandBuffer cmdBuffer, bool wireframe) void render(VkCommandBuffer cmdBuffer, bool wireframe)
{ {
VkDeviceSize offsets[1] = { 0 }; VkDeviceSize offsets[1] = { 0 };
// Bind scene vertex and index buffers
vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, offsets);
vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
for (size_t i = 0; i < meshes.size(); i++) for (size_t i = 0; i < meshes.size(); i++)
{ {
if ((renderSingleScenePart) && (i != scenePartIndex)) if ((renderSingleScenePart) && (i != scenePartIndex))
continue; continue;
//if (meshes[i].material->opacity == 0.0f)
// continue;
// todo : per material pipelines // todo : per material pipelines
// vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *mesh.material->pipeline); // vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *mesh.material->pipeline);
// We will be using multiple descriptor sets for rendering // We will be using multiple descriptor sets for rendering
// In GLSL the selection is done via the set and binding keywords // In GLSL the selection is done via the set and binding keywords
@ -519,13 +537,9 @@ public:
sizeof(SceneMaterialProperites), sizeof(SceneMaterialProperites),
&meshes[i].material->properties); &meshes[i].material->properties);
vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &meshes[i].vertexBuffer.buffer, offsets); // Render from the global scene vertex buffer using the mesh index offset
vkCmdBindIndexBuffer(cmdBuffer, meshes[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(cmdBuffer, meshes[i].indexCount, 1, 0, meshes[i].indexBase, 0);
vkCmdDrawIndexed(cmdBuffer, meshes[i].indexCount, 1, 0, 0, 0);
} }
// Render transparent objects last
} }
}; };
@ -653,9 +667,9 @@ public:
sizeof(float) * 8); sizeof(float) * 8);
vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
vertices.inputState.vertexBindingDescriptionCount = vertices.bindingDescriptions.size(); vertices.inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.bindingDescriptions.size());
vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
vertices.inputState.vertexAttributeDescriptionCount = vertices.attributeDescriptions.size(); vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.attributeDescriptions.size());
vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
} }
@ -705,7 +719,7 @@ public:
VkPipelineDynamicStateCreateInfo dynamicState = VkPipelineDynamicStateCreateInfo dynamicState =
vkTools::initializers::pipelineDynamicStateCreateInfo( vkTools::initializers::pipelineDynamicStateCreateInfo(
dynamicStateEnables.data(), dynamicStateEnables.data(),
dynamicStateEnables.size(), static_cast<uint32_t>(dynamicStateEnables.size()),
0); 0);
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages; std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
@ -728,7 +742,7 @@ public:
pipelineCreateInfo.pViewportState = &viewportState; pipelineCreateInfo.pViewportState = &viewportState;
pipelineCreateInfo.pDepthStencilState = &depthStencilState; pipelineCreateInfo.pDepthStencilState = &depthStencilState;
pipelineCreateInfo.pDynamicState = &dynamicState; pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.stageCount = shaderStages.size(); pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCreateInfo.pStages = shaderStages.data(); pipelineCreateInfo.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &scene->pipelines.solid)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &scene->pipelines.solid));