Code cleanup

This commit is contained in:
Sascha Willems 2024-09-14 20:27:08 +02:00
parent 50a3f5f820
commit c7eed13c7c

View file

@ -339,11 +339,11 @@ public:
vkFreeMemory(device, stagingBuffers.indexBuffer.memory, nullptr); vkFreeMemory(device, stagingBuffers.indexBuffer.memory, nullptr);
} }
// Descriptors are allocated from a pool, that tells the implementation how many and what types of descriptors we are going to use (at maximum) // Decriptors are used to pass data to shaders, for our sample we use a descriptor to pass parameters like matrices to the shader
void createDescriptorPool() void createDescriptors()
{ {
// We need to tell the API the number of max. requested descriptors per type // Descriptors are allocated from a pool, that tells the implementation how many and what types of descriptors we are going to use (at maximum)
VkDescriptorPoolSize descriptorTypeCounts[1]; VkDescriptorPoolSize descriptorTypeCounts[1]{};
// This example only one descriptor type (uniform buffer) // This example only one descriptor type (uniform buffer)
descriptorTypeCounts[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorTypeCounts[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
// We have one buffer (and as such descriptor) per frame // We have one buffer (and as such descriptor) per frame
@ -362,13 +362,10 @@ public:
// Our sample will create one set per uniform buffer per frame // Our sample will create one set per uniform buffer per frame
descriptorPoolCI.maxSets = MAX_CONCURRENT_FRAMES; descriptorPoolCI.maxSets = MAX_CONCURRENT_FRAMES;
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool));
}
// Descriptor set layouts define the interface between our application and the shader // Descriptor set layouts define the interface between our application and the shader
// Basically connects the different shader stages to descriptors for binding uniform buffers, image samplers, etc. // Basically connects the different shader stages to descriptors for binding uniform buffers, image samplers, etc.
// So every shader binding should map to one descriptor set layout binding // So every shader binding should map to one descriptor set layout binding
void createDescriptorSetLayout()
{
// Binding 0: Uniform buffer (Vertex shader) // Binding 0: Uniform buffer (Vertex shader)
VkDescriptorSetLayoutBinding layoutBinding{}; VkDescriptorSetLayoutBinding layoutBinding{};
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@ -380,19 +377,8 @@ public:
descriptorLayoutCI.pBindings = &layoutBinding; descriptorLayoutCI.pBindings = &layoutBinding;
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCI, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCI, nullptr, &descriptorSetLayout));
// Create the pipeline layout that is used to generate the rendering pipelines that are based on this descriptor set layout // Where the descriptor set layout is the interface, the descriptor set points to actual data
// In a more complex scenario you would have different pipeline layouts for different descriptor set layouts that could be reused // Descriptors that are changed per frame need to be multiplied, so we can update descriptor n+1 while n is still used by the GPU, so we create one per max frame in flight
VkPipelineLayoutCreateInfo pipelineLayoutCI{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pipelineLayoutCI.setLayoutCount = 1;
pipelineLayoutCI.pSetLayouts = &descriptorSetLayout;
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
}
// Shaders access data using descriptor sets that "point" at our uniform buffers
// The descriptor sets make use of the descriptor set layouts created above
void createDescriptorSets()
{
// Allocate one descriptor set per frame from the global descriptor pool
for (uint32_t i = 0; i < MAX_CONCURRENT_FRAMES; i++) { for (uint32_t i = 0; i < MAX_CONCURRENT_FRAMES; i++) {
VkDescriptorSetAllocateInfo allocInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; VkDescriptorSetAllocateInfo allocInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
allocInfo.descriptorPool = descriptorPool; allocInfo.descriptorPool = descriptorPool;
@ -516,12 +502,17 @@ public:
} }
} }
void createPipelines() void createPipeline()
{ {
// The pipeline layout is the interface telling the pipeline what type of descriptors will later be bound
VkPipelineLayoutCreateInfo pipelineLayoutCI{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pipelineLayoutCI.setLayoutCount = 1;
pipelineLayoutCI.pSetLayouts = &descriptorSetLayout;
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
// Create the graphics pipeline used in this example // Create the graphics pipeline used in this example
// Vulkan uses the concept of rendering pipelines to encapsulate fixed states, replacing OpenGL's complex state machine // Vulkan uses the concept of rendering pipelines to encapsulate fixed states, replacing OpenGL's complex state machine
// A pipeline is then stored and hashed on the GPU making pipeline changes very fast // A pipeline is then stored and hashed on the GPU making pipeline changes very fast
// Note: There are still a few dynamic states that are not directly part of the pipeline (but the info that they are used is)
VkGraphicsPipelineCreateInfo pipelineCI{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; VkGraphicsPipelineCreateInfo pipelineCI{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
// The layout used for this pipeline (can be shared among multiple pipelines using the same layout) // The layout used for this pipeline (can be shared among multiple pipelines using the same layout)
@ -560,12 +551,9 @@ public:
viewportStateCI.scissorCount = 1; viewportStateCI.scissorCount = 1;
// Enable dynamic states // Enable dynamic states
// Most states are baked into the pipeline, but there are still a few dynamic states that can be changed within a command buffer // Most states are baked into the pipeline, but there is somee state that can be dynamically changed within the command buffer to mak e things easuer
// To be able to change these we need do specify which dynamic states will be changed using this pipeline. Their actual states are set later on in the command buffer. // To be able to change these we need do specify which dynamic states will be changed using this pipeline. Their actual states are set later on in the command buffer
// For this example we will set the viewport and scissor using dynamic states std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
std::vector<VkDynamicState> dynamicStateEnables;
dynamicStateEnables.push_back(VK_DYNAMIC_STATE_VIEWPORT);
dynamicStateEnables.push_back(VK_DYNAMIC_STATE_SCISSOR);
VkPipelineDynamicStateCreateInfo dynamicStateCI{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; VkPipelineDynamicStateCreateInfo dynamicStateCI{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size()); dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
@ -583,11 +571,9 @@ public:
depthStencilStateCI.stencilTestEnable = VK_FALSE; depthStencilStateCI.stencilTestEnable = VK_FALSE;
depthStencilStateCI.front = depthStencilStateCI.back; depthStencilStateCI.front = depthStencilStateCI.back;
// Multi sampling state
// This example does not make use of multi sampling (for anti-aliasing), the state must still be set and passed to the pipeline // This example does not make use of multi sampling (for anti-aliasing), the state must still be set and passed to the pipeline
VkPipelineMultisampleStateCreateInfo multisampleStateCI{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; VkPipelineMultisampleStateCreateInfo multisampleStateCI{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampleStateCI.pSampleMask = nullptr;
// Vertex input descriptions // Vertex input descriptions
// Specifies the vertex input parameters for a pipeline // Specifies the vertex input parameters for a pipeline
@ -645,12 +631,12 @@ public:
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size()); pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCI.pStages = shaderStages.data(); pipelineCI.pStages = shaderStages.data();
// New create info to define color, depth and stencil attachments at pipeline create time // Attachment information for dynamic rendering
VkPipelineRenderingCreateInfoKHR pipelineRenderingCreateInfo{ VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR }; VkPipelineRenderingCreateInfoKHR pipelineRenderingCI{ VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR };
pipelineRenderingCreateInfo.colorAttachmentCount = 1; pipelineRenderingCI.colorAttachmentCount = 1;
pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChain.colorFormat; pipelineRenderingCI.pColorAttachmentFormats = &swapChain.colorFormat;
pipelineRenderingCreateInfo.depthAttachmentFormat = depthFormat; pipelineRenderingCI.depthAttachmentFormat = depthFormat;
pipelineRenderingCreateInfo.stencilAttachmentFormat = depthFormat; pipelineRenderingCI.stencilAttachmentFormat = depthFormat;
// Assign the pipeline states to the pipeline creation info structure // Assign the pipeline states to the pipeline creation info structure
pipelineCI.pVertexInputState = &vertexInputStateCI; pipelineCI.pVertexInputState = &vertexInputStateCI;
@ -661,12 +647,12 @@ public:
pipelineCI.pViewportState = &viewportStateCI; pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pDepthStencilState = &depthStencilStateCI; pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCI.pDynamicState = &dynamicStateCI; pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCI.pNext = &pipelineRenderingCreateInfo; pipelineCI.pNext = &pipelineRenderingCI;
// Create rendering pipeline using the specified states // Create rendering pipeline using the specified states
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
// Shader modules are no longer needed once the graphics pipeline has been created // Shader modules can safely be destroyed when the pipeline has been created
vkDestroyShaderModule(device, shaderStages[0].module, nullptr); vkDestroyShaderModule(device, shaderStages[0].module, nullptr);
vkDestroyShaderModule(device, shaderStages[1].module, nullptr); vkDestroyShaderModule(device, shaderStages[1].module, nullptr);
} }
@ -675,7 +661,6 @@ public:
{ {
// Prepare and initialize the per-frame uniform buffer blocks containing shader uniforms // Prepare and initialize the per-frame uniform buffer blocks containing shader uniforms
// Single uniforms like in OpenGL are no longer present in Vulkan. All Shader uniforms are passed via uniform buffer blocks // Single uniforms like in OpenGL are no longer present in Vulkan. All Shader uniforms are passed via uniform buffer blocks
VkMemoryRequirements memReqs;
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VkBufferCreateInfo bufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; VkBufferCreateInfo bufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
@ -691,6 +676,7 @@ public:
for (uint32_t i = 0; i < MAX_CONCURRENT_FRAMES; i++) { for (uint32_t i = 0; i < MAX_CONCURRENT_FRAMES; i++) {
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferInfo, nullptr, &uniformBuffers[i].handle)); VK_CHECK_RESULT(vkCreateBuffer(device, &bufferInfo, nullptr, &uniformBuffers[i].handle));
// Get memory requirements including size, alignment and memory type // Get memory requirements including size, alignment and memory type
VkMemoryRequirements memReqs;
vkGetBufferMemoryRequirements(device, uniformBuffers[i].handle, &memReqs); vkGetBufferMemoryRequirements(device, uniformBuffers[i].handle, &memReqs);
allocInfo.allocationSize = memReqs.size; allocInfo.allocationSize = memReqs.size;
// Get the memory type index that supports host visible memory access // Get the memory type index that supports host visible memory access
@ -714,18 +700,13 @@ public:
createCommandBuffers(); createCommandBuffers();
createVertexBuffer(); createVertexBuffer();
createUniformBuffers(); createUniformBuffers();
createDescriptorSetLayout(); createDescriptors();
createDescriptorPool(); createPipeline();
createDescriptorSets();
createPipelines();
prepared = true; prepared = true;
} }
virtual void render() virtual void render() override
{ {
if (!prepared)
return;
// Use a fence to wait until the command buffer has finished execution before using it again // Use a fence to wait until the command buffer has finished execution before using it again
vkWaitForFences(device, 1, &waitFences[currentFrame], VK_TRUE, UINT64_MAX); vkWaitForFences(device, 1, &waitFences[currentFrame], VK_TRUE, UINT64_MAX);
VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[currentFrame])); VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[currentFrame]));