From fc3e535074e15fae1cdefec12cbbbfc25b33c52a Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Mon, 8 Jan 2024 19:59:28 +0100 Subject: [PATCH] Code cleanup up, code comments, fix validation --- examples/dynamicstate/dynamicstate.cpp | 247 +++++++++++-------------- 1 file changed, 108 insertions(+), 139 deletions(-) diff --git a/examples/dynamicstate/dynamicstate.cpp b/examples/dynamicstate/dynamicstate.cpp index bd188ef4..5cfe4b9d 100644 --- a/examples/dynamicstate/dynamicstate.cpp +++ b/examples/dynamicstate/dynamicstate.cpp @@ -1,5 +1,10 @@ /* * Vulkan Example - Using dynamic state +* +* This sample demonstrates the use of some of the VK_EXT_dynamic_state extensions +* These allow an application to set some pipeline related state dynamically at drawtime +* instead of having to pre-bake the state into a pipeline +* This can help reduce the number of pipelines required * * Copyright (C) 2022-2023 by Sascha Willems - www.saschawillems.de * @@ -9,34 +14,30 @@ #include "vulkanexamplebase.h" #include "VulkanglTFModel.h" - class VulkanExample: public VulkanExampleBase { public: vkglTF::Model scene; - vks::Buffer uniformBuffer; - - // Same uniform buffer layout as shader - struct UBOVS { + struct UniformData { glm::mat4 projection; glm::mat4 modelView; - glm::vec4 lightPos = glm::vec4(0.0f, 2.0f, 1.0f, 0.0f); - } uboVS; + glm::vec4 lightPos{ 0.0f, 2.0f, 1.0f, 0.0f }; + } uniformData; + vks::Buffer uniformBuffer; float clearColor[4] = { 0.0f, 0.0f, 0.2f, 1.0f }; - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; - - VkPipeline pipeline; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkPipeline pipeline{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; // This sample demonstrates different dynamic states, so we check and store what extension is available - bool hasDynamicState = false; - bool hasDynamicState2 = false; - bool hasDynamicState3 = false; - bool hasDynamicVertexState = false; + bool hasDynamicState{ false }; + bool hasDynamicState2{ false }; + bool hasDynamicState3{ false }; + bool hasDynamicVertexState{ false }; VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extendedDynamicStateFeaturesEXT{}; VkPhysicalDeviceExtendedDynamicState2FeaturesEXT extendedDynamicState2FeaturesEXT{}; @@ -44,15 +45,15 @@ public: // Function pointers for dynamic states used in this sample // VK_EXT_dynamic_stte - PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT = nullptr; - PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT = nullptr; - PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT = nullptr; - PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT = nullptr; + PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{ nullptr }; + PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{ nullptr }; + PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{ nullptr }; + PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{ nullptr }; // VK_EXT_dynamic_state_2 - PFN_vkCmdSetRasterizerDiscardEnable vkCmdSetRasterizerDiscardEnableEXT = nullptr; + PFN_vkCmdSetRasterizerDiscardEnable vkCmdSetRasterizerDiscardEnableEXT{ nullptr }; // VK_EXT_dynamic_state_3 - PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT = nullptr; - PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT = nullptr; + PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{ nullptr }; + PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{ nullptr }; // Dynamic state UI toggles struct DynamicState { @@ -76,15 +77,63 @@ public: camera.setRotation(glm::vec3(-25.0f, 15.0f, 0.0f)); camera.setRotationSpeed(0.5f); camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); + + // Note: We enable the dynamic state extensions dynamically, based on which ones the device supports see getEnabledExtensions enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); } ~VulkanExample() { - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + if (device) { + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + } + } - uniformBuffer.destroy(); + void getEnabledExtensions() + { + // Check what dynamic states are supported by the current implementation + hasDynamicState = vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + hasDynamicState2 = vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); + hasDynamicState3 = vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + hasDynamicVertexState = vulkanDevice->extensionSupported(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + + // Enable dynamic state extensions if present. This function is called after physical and before logical device creation, so we can enabled extensions based on a list of supported extensions + if (vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)) { + enabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + extendedDynamicStateFeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; + extendedDynamicStateFeaturesEXT.extendedDynamicState = VK_TRUE; + deviceCreatepNextChain = &extendedDynamicStateFeaturesEXT; + } + if (vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME)) { + enabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); + extendedDynamicState2FeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT; + extendedDynamicState2FeaturesEXT.extendedDynamicState2 = VK_TRUE; + if (hasDynamicState) { + extendedDynamicStateFeaturesEXT.pNext = &extendedDynamicState2FeaturesEXT; + } + else { + deviceCreatepNextChain = &extendedDynamicState2FeaturesEXT; + } + } + if (vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME)) { + enabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + extendedDynamicState3FeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT; + extendedDynamicState3FeaturesEXT.extendedDynamicState3ColorBlendEnable = VK_TRUE; + extendedDynamicState3FeaturesEXT.extendedDynamicState3ColorBlendEquation = VK_TRUE; + if (hasDynamicState2) { + extendedDynamicState2FeaturesEXT.pNext = &extendedDynamicState3FeaturesEXT; + } + else { + deviceCreatepNextChain = &extendedDynamicState3FeaturesEXT; + } + + } + if (vulkanDevice->extensionSupported(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME)) { + enabledDeviceExtensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + } } void buildCommandBuffers() @@ -176,60 +225,41 @@ public: scene.loadFromFile(getAssetPath() + "models/treasure_smooth.gltf", vulkanDevice, queue, glTFLoadingFlags); } - void setupDescriptorPool() + void setupDescriptors() { - std::vector poolSizes = - { + // Pool + std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1) }; - VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { + // Layout + std::vector setLayoutBindings = { // Binding 0 : Vertex shader uniform buffer - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_VERTEX_BIT, - 0) + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0) }; - VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - void setupDescriptorSet() - { - VkDescriptorSetAllocateInfo allocInfo = - vks::initializers::descriptorSetAllocateInfo( - descriptorPool, - &descriptorSetLayout, - 1); + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); - - std::vector writeDescriptorSets = - { + std::vector writeDescriptorSets = { // Binding 0 : Vertex shader uniform buffer - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniformBuffer.descriptor) + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor) }; - vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } void preparePipelines() { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Pipeline + // Instead of having to create a pipeline for each state combination, we only create one pipeline and toggle the new dynamic states during command buffer creation VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); @@ -239,6 +269,7 @@ public: VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); std::array shaderStages; + // All dynamic states we want to use need to be enabled at pipeline creation std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_LINE_WIDTH, }; if (hasDynamicState) { dynamicStateEnables.push_back(VK_DYNAMIC_STATE_CULL_MODE_EXT); @@ -270,8 +301,6 @@ public: // Create the graphics pipeline state objects - // Textured pipeline - // Phong shading pipeline shaderStages[0] = loadShader(getShadersPath() + "pipelines/phong.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getShadersPath() + "pipelines/phong.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); @@ -281,82 +310,22 @@ public: void prepareUniformBuffers() { // Create the vertex shader uniform buffer block - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBuffer, - sizeof(uboVS))); - - // Map persistent + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData))); VK_CHECK_RESULT(uniformBuffer.map()); - - updateUniformBuffers(); } void updateUniformBuffers() { - uboVS.projection = camera.matrices.perspective; - uboVS.modelView = camera.matrices.view; - memcpy(uniformBuffer.mapped, &uboVS, sizeof(uboVS)); - } - - void draw() - { - VulkanExampleBase::prepareFrame(); - - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); - - VulkanExampleBase::submitFrame(); - } - - void getEnabledExtensions() - { - // Check what dynamic states are supported by the current implementation - hasDynamicState = vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - hasDynamicState2 = vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - hasDynamicState3 = vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - hasDynamicVertexState = vulkanDevice->extensionSupported(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); - - // Enable dynamic state extensions if present. This function is called after physical and before logical device creation, so we can enabled extensions based on a list of supported extensions - if (vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)) { - enabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - extendedDynamicStateFeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; - extendedDynamicStateFeaturesEXT.extendedDynamicState = VK_TRUE; - deviceCreatepNextChain = &extendedDynamicStateFeaturesEXT; - } - if (vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME)) { - enabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - extendedDynamicState2FeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT; - extendedDynamicState2FeaturesEXT.extendedDynamicState2 = VK_TRUE; - if (hasDynamicState) { - extendedDynamicStateFeaturesEXT.pNext = &extendedDynamicState2FeaturesEXT; - } else { - deviceCreatepNextChain = &extendedDynamicState2FeaturesEXT; - } - } - if (vulkanDevice->extensionSupported(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME)) { - enabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - extendedDynamicState3FeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT; - extendedDynamicState3FeaturesEXT.extendedDynamicState3ColorBlendEnable = VK_TRUE; - extendedDynamicState3FeaturesEXT.extendedDynamicState3ColorBlendEquation = VK_TRUE; - if (hasDynamicState2) { - extendedDynamicState2FeaturesEXT.pNext = &extendedDynamicState3FeaturesEXT; - } else { - deviceCreatepNextChain = &extendedDynamicState3FeaturesEXT; - } - - } - if (vulkanDevice->extensionSupported(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME)) { - enabledDeviceExtensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); - } + uniformData.projection = camera.matrices.perspective; + uniformData.modelView = camera.matrices.view; + memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData)); } void prepare() { VulkanExampleBase::prepare(); + // Dynamic states are set with vkCmd* calls in the command buffer, so we need to load the function pointers depending on extension supports if (hasDynamicState) { vkCmdSetCullModeEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetCullModeEXT")); vkCmdSetFrontFaceEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetFrontFaceEXT")); @@ -375,27 +344,27 @@ public: loadAssets(); prepareUniformBuffers(); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSet(); buildCommandBuffers(); prepared = true; } + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + virtual void render() { if (!prepared) return; - draw(); - if (camera.updated) { - updateUniformBuffers(); - } - } - - virtual void viewChanged() - { updateUniformBuffers(); + draw(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)