mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-28 09:33:52 +00:00
Create a VkPipelineCache at device init, loaded from disk if available. All 65 pipeline creation calls across 19 renderer files now use the shared cache. On shutdown, the cache is serialized to disk so subsequent launches skip redundant shader compilation. Cache path: ~/.local/share/wowee/pipeline_cache.bin (Linux), ~/Library/Caches/wowee/ (macOS), %APPDATA%\wowee\ (Windows). Stale/corrupt caches are handled gracefully (fallback to empty cache).
291 lines
11 KiB
C++
291 lines
11 KiB
C++
#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;
|
|
}
|
|
|
|
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<VkDynamicState>& states) {
|
|
dynamicStates_ = states;
|
|
return *this;
|
|
}
|
|
|
|
VkPipeline PipelineBuilder::build(VkDevice device, VkPipelineCache cache) 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_;
|
|
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<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, cache, 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<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
|