#include "rendering/vk_pipeline.hpp" #include "core/logger.hpp" namespace wowee { namespace rendering { PipelineBuilder::PipelineBuilder() { // Default: one blend attachment with blending disabled colorBlendAttachments_.push_back(blendDisabled()); // Default dynamic states: viewport + scissor (almost always dynamic) dynamicStates_ = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; } PipelineBuilder& PipelineBuilder::setShaders( VkPipelineShaderStageCreateInfo vert, VkPipelineShaderStageCreateInfo frag) { shaderStages_ = {vert, frag}; return *this; } PipelineBuilder& PipelineBuilder::setVertexInput( const std::vector& bindings, const std::vector& attributes) { vertexBindings_ = bindings; vertexAttributes_ = attributes; return *this; } PipelineBuilder& PipelineBuilder::setNoVertexInput() { vertexBindings_.clear(); vertexAttributes_.clear(); return *this; } PipelineBuilder& PipelineBuilder::setTopology(VkPrimitiveTopology topology, VkBool32 primitiveRestart) { topology_ = topology; primitiveRestart_ = primitiveRestart; return *this; } PipelineBuilder& PipelineBuilder::setRasterization(VkPolygonMode polygonMode, VkCullModeFlags cullMode, VkFrontFace frontFace) { polygonMode_ = polygonMode; cullMode_ = cullMode; frontFace_ = frontFace; return *this; } PipelineBuilder& PipelineBuilder::setDepthTest(bool enable, bool writeEnable, VkCompareOp compareOp) { depthTestEnable_ = enable; depthWriteEnable_ = writeEnable; depthCompareOp_ = compareOp; return *this; } PipelineBuilder& PipelineBuilder::setNoDepthTest() { depthTestEnable_ = false; depthWriteEnable_ = false; return *this; } PipelineBuilder& PipelineBuilder::setDepthBias(float constantFactor, float slopeFactor) { depthBiasEnable_ = true; depthBiasConstant_ = constantFactor; depthBiasSlope_ = slopeFactor; return *this; } PipelineBuilder& PipelineBuilder::setColorBlendAttachment( VkPipelineColorBlendAttachmentState blendState) { colorBlendAttachments_ = {blendState}; return *this; } PipelineBuilder& PipelineBuilder::setNoColorAttachment() { colorBlendAttachments_.clear(); return *this; } PipelineBuilder& PipelineBuilder::setMultisample(VkSampleCountFlagBits samples) { msaaSamples_ = samples; return *this; } PipelineBuilder& PipelineBuilder::setAlphaToCoverage(bool enable) { alphaToCoverage_ = enable; return *this; } PipelineBuilder& PipelineBuilder::setLayout(VkPipelineLayout layout) { pipelineLayout_ = layout; return *this; } PipelineBuilder& PipelineBuilder::setRenderPass(VkRenderPass renderPass, uint32_t subpass) { renderPass_ = renderPass; subpass_ = subpass; return *this; } PipelineBuilder& PipelineBuilder::setDynamicStates(const std::vector& states) { dynamicStates_ = states; return *this; } VkPipeline PipelineBuilder::build(VkDevice device) const { // Vertex input VkPipelineVertexInputStateCreateInfo vertexInput{}; vertexInput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInput.vertexBindingDescriptionCount = static_cast(vertexBindings_.size()); vertexInput.pVertexBindingDescriptions = vertexBindings_.data(); vertexInput.vertexAttributeDescriptionCount = static_cast(vertexAttributes_.size()); vertexInput.pVertexAttributeDescriptions = vertexAttributes_.data(); // Input assembly VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssembly.topology = topology_; inputAssembly.primitiveRestartEnable = primitiveRestart_; // Viewport / scissor (dynamic, so just specify count) VkPipelineViewportStateCreateInfo viewportState{}; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount = 1; viewportState.scissorCount = 1; // Rasterization VkPipelineRasterizationStateCreateInfo rasterizer{}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = polygonMode_; rasterizer.lineWidth = 1.0f; rasterizer.cullMode = cullMode_; rasterizer.frontFace = frontFace_; rasterizer.depthBiasEnable = depthBiasEnable_ ? VK_TRUE : VK_FALSE; rasterizer.depthBiasConstantFactor = depthBiasConstant_; rasterizer.depthBiasSlopeFactor = depthBiasSlope_; // Multisampling VkPipelineMultisampleStateCreateInfo multisampling{}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable = VK_FALSE; multisampling.rasterizationSamples = msaaSamples_; multisampling.alphaToCoverageEnable = alphaToCoverage_ ? VK_TRUE : VK_FALSE; // Depth/stencil VkPipelineDepthStencilStateCreateInfo depthStencil{}; depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depthStencil.depthTestEnable = depthTestEnable_ ? VK_TRUE : VK_FALSE; depthStencil.depthWriteEnable = depthWriteEnable_ ? VK_TRUE : VK_FALSE; depthStencil.depthCompareOp = depthCompareOp_; depthStencil.depthBoundsTestEnable = VK_FALSE; depthStencil.stencilTestEnable = VK_FALSE; // Color blending VkPipelineColorBlendStateCreateInfo colorBlending{}; colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.logicOpEnable = VK_FALSE; colorBlending.attachmentCount = static_cast(colorBlendAttachments_.size()); colorBlending.pAttachments = colorBlendAttachments_.data(); // Dynamic state VkPipelineDynamicStateCreateInfo dynamicState{}; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.dynamicStateCount = static_cast(dynamicStates_.size()); dynamicState.pDynamicStates = dynamicStates_.data(); // Create pipeline VkGraphicsPipelineCreateInfo pipelineInfo{}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.stageCount = static_cast(shaderStages_.size()); pipelineInfo.pStages = shaderStages_.data(); pipelineInfo.pVertexInputState = &vertexInput; pipelineInfo.pInputAssemblyState = &inputAssembly; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pDepthStencilState = &depthStencil; pipelineInfo.pColorBlendState = colorBlendAttachments_.empty() ? nullptr : &colorBlending; pipelineInfo.pDynamicState = dynamicStates_.empty() ? nullptr : &dynamicState; pipelineInfo.layout = pipelineLayout_; pipelineInfo.renderPass = renderPass_; pipelineInfo.subpass = subpass_; VkPipeline pipeline = VK_NULL_HANDLE; if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS) { LOG_ERROR("Failed to create graphics pipeline"); return VK_NULL_HANDLE; } return pipeline; } VkPipelineColorBlendAttachmentState PipelineBuilder::blendDisabled() { VkPipelineColorBlendAttachmentState state{}; state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; state.blendEnable = VK_FALSE; return state; } VkPipelineColorBlendAttachmentState PipelineBuilder::blendAlpha() { VkPipelineColorBlendAttachmentState state{}; state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; state.blendEnable = VK_TRUE; state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; state.colorBlendOp = VK_BLEND_OP_ADD; state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; state.alphaBlendOp = VK_BLEND_OP_ADD; return state; } VkPipelineColorBlendAttachmentState PipelineBuilder::blendPremultiplied() { VkPipelineColorBlendAttachmentState state{}; state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; state.blendEnable = VK_TRUE; state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; state.colorBlendOp = VK_BLEND_OP_ADD; state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; state.alphaBlendOp = VK_BLEND_OP_ADD; return state; } VkPipelineColorBlendAttachmentState PipelineBuilder::blendAdditive() { VkPipelineColorBlendAttachmentState state{}; state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; state.blendEnable = VK_TRUE; state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; state.colorBlendOp = VK_BLEND_OP_ADD; state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; state.alphaBlendOp = VK_BLEND_OP_ADD; return state; } VkPipelineLayout createPipelineLayout(VkDevice device, const std::vector& setLayouts, const std::vector& pushConstants) { VkPipelineLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layoutInfo.setLayoutCount = static_cast(setLayouts.size()); layoutInfo.pSetLayouts = setLayouts.data(); layoutInfo.pushConstantRangeCount = static_cast(pushConstants.size()); layoutInfo.pPushConstantRanges = pushConstants.data(); VkPipelineLayout layout = VK_NULL_HANDLE; if (vkCreatePipelineLayout(device, &layoutInfo, nullptr, &layout) != VK_SUCCESS) { LOG_ERROR("Failed to create pipeline layout"); } return layout; } VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device, const std::vector& bindings) { VkDescriptorSetLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); VkDescriptorSetLayout layout = VK_NULL_HANDLE; if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &layout) != VK_SUCCESS) { LOG_ERROR("Failed to create descriptor set layout"); } return layout; } } // namespace rendering } // namespace wowee