2026-02-21 19:41:21 -08:00
|
|
|
#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<VkVertexInputBindingDescription>& bindings,
|
|
|
|
|
const std::vector<VkVertexInputAttributeDescription>& 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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 22:34:48 -08:00
|
|
|
PipelineBuilder& PipelineBuilder::setAlphaToCoverage(bool enable) {
|
|
|
|
|
alphaToCoverage_ = enable;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
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<VkDynamicState>& 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<uint32_t>(vertexBindings_.size());
|
|
|
|
|
vertexInput.pVertexBindingDescriptions = vertexBindings_.data();
|
|
|
|
|
vertexInput.vertexAttributeDescriptionCount = static_cast<uint32_t>(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_;
|
2026-02-22 22:34:48 -08:00
|
|
|
multisampling.alphaToCoverageEnable = alphaToCoverage_ ? VK_TRUE : VK_FALSE;
|
2026-02-21 19:41:21 -08:00
|
|
|
|
|
|
|
|
// 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<uint32_t>(colorBlendAttachments_.size());
|
|
|
|
|
colorBlending.pAttachments = colorBlendAttachments_.data();
|
|
|
|
|
|
|
|
|
|
// Dynamic state
|
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamicState{};
|
|
|
|
|
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
|
|
|
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates_.size());
|
|
|
|
|
dynamicState.pDynamicStates = dynamicStates_.data();
|
|
|
|
|
|
|
|
|
|
// Create pipeline
|
|
|
|
|
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
|
|
|
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
|
|
|
pipelineInfo.stageCount = static_cast<uint32_t>(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;
|
2026-02-22 22:34:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2026-02-21 19:41:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<VkDescriptorSetLayout>& setLayouts,
|
|
|
|
|
const std::vector<VkPushConstantRange>& pushConstants)
|
|
|
|
|
{
|
|
|
|
|
VkPipelineLayoutCreateInfo layoutInfo{};
|
|
|
|
|
layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
|
|
|
layoutInfo.setLayoutCount = static_cast<uint32_t>(setLayouts.size());
|
|
|
|
|
layoutInfo.pSetLayouts = setLayouts.data();
|
|
|
|
|
layoutInfo.pushConstantRangeCount = static_cast<uint32_t>(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<VkDescriptorSetLayoutBinding>& bindings)
|
|
|
|
|
{
|
|
|
|
|
VkDescriptorSetLayoutCreateInfo layoutInfo{};
|
|
|
|
|
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
|
|
|
layoutInfo.bindingCount = static_cast<uint32_t>(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
|