#include <volk.h>
#include <cstdio>
#include <vector>
#include "backend/vulkan/debug.h"
#include "backend/vulkan/utils.h"
#include "backend/vulkan/vk_api.h"
#include "backend/vulkan/vk_conversion.h"
#include "shaders/renderer_types.h"
namespace rhi {
using namespace vk;
RenderPipelineDesc &
RenderPipelineDesc::set_shaders(ShaderCompiler &sc, const char *path, const char *vs, const char *fs) {
assert(device != nullptr && "RenderPipelineDesc::device must be assigned before set_shaders");
shader_modules.clear();
shader_modules.push_back(load_shader(sc, *device, path, vs, ShaderStage::VERTEX));
shader_modules.push_back(load_shader(sc, *device, path, fs, ShaderStage::FRAGMENT));
shader_path = path;
vs_entry = vs;
fs_entry = fs;
return *this;
}
ComputePipelineDesc &ComputePipelineDesc::set_shader(ShaderCompiler &sc, const char *path, const char *cs) {
assert(device != nullptr && "ComputePipelineDesc::device must be assigned before set_shader");
shader_modules.clear();
shader_modules.push_back(load_shader(sc, *device, path, cs, ShaderStage::COMPUTE));
shader_path = path;
cs_entry = cs;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_input_topology(rhi::PipelineTopology topology) {
this->topology = topology;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_polygon_mode(rhi::PipelineFillMode mode) {
fill_mode = mode;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_color_format(ImageFormat format) {
color_attachment_formats.push_back(format);
return *this;
};
RenderPipelineDesc &RenderPipelineDesc::add_vertex_binding(u32 binding, u32 stride, bool is_instanced) {
vertex_bindings.push_back({binding, stride, is_instanced});
return *this;
}
RenderPipelineDesc &
RenderPipelineDesc::add_vertex_attribute(u32 location, u32 binding, ImageFormat format, u32 offset) {
vertex_attributes.push_back({location, binding, format, offset});
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_depth_format(ImageFormat format) {
depth_attachment_format = format;
return *this;
};
RenderPipelineDesc &
RenderPipelineDesc::set_cull_mode(rhi::PipelineCullMode mode, rhi::PipelineTriangleWindingOrder winding) {
cull_mode = mode;
this->winding = winding;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_multisampling(rhi::SampleCount count) {
samples = count;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_depth_testing(bool do_test, bool do_write, rhi::PipelineCompareOp op) {
depth_test_enable = do_test;
depth_write_enable = do_write;
depth_compare_op = op;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_depth_bias(bool enabled, f32 constant, f32 clamp, f32 slope) {
depth_bias_enable = enabled;
depth_bias_constant = constant;
depth_bias_clamp = clamp;
depth_bias_slope = slope;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_depth_clamp(bool enabled) {
depth_clamp_enable = enabled;
return *this;
}
RenderPipelineDesc &RenderPipelineDesc::set_blending(
rhi::PipelineBlendFactor src_color,
rhi::PipelineBlendFactor dst_color,
rhi::PipelineBlendOp color_op,
rhi::PipelineBlendFactor src_alpha,
rhi::PipelineBlendFactor dst_alpha,
rhi::PipelineBlendOp alpha_op
) {
blend_enable = true;
src_color_blend_factor = src_color;
dst_color_blend_factor = dst_color;
color_blend_op = color_op;
src_alpha_blend_factor = src_alpha;
dst_alpha_blend_factor = dst_alpha;
alpha_blend_op = alpha_op;
return *this;
}
RenderPipeline RenderPipelineDesc::build() {
assert(device != nullptr && "RenderPipelineDesc::device must be assigned before build");
if (shader_modules.empty()) {
VEL_ERROR("Cannot build render pipeline: no shader modules set");
return {};
}
std::vector<VkPipelineShaderStageCreateInfo> shader_stages;
for (auto &mod : shader_modules) {
VkShaderModule vk_mod = mod.handle;
if (vk_mod == VK_NULL_HANDLE) {
VEL_ERROR("Cannot build render pipeline: one or more shader modules are null");
for (auto &stage : shader_stages) {
vkDestroyShaderModule(device->logical, stage.module, nullptr);
}
return {};
}
VkPipelineShaderStageCreateInfo stage_info = {};
stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage_info.stage = static_cast<VkShaderStageFlagBits>(vk::shader_stage_to_vk(mod.stage));
stage_info.module = vk_mod;
stage_info.pName = "main";
shader_stages.push_back(stage_info);
}
RenderPipeline result;
result.device = device;
{
VkDynamicState dynamic_states[8];
u32 ds_count = 0;
dynamic_states[ds_count++] = VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT;
dynamic_states[ds_count++] = VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT;
if (enable_dynamic_polygon_mode) {
dynamic_states[ds_count++] = VK_DYNAMIC_STATE_POLYGON_MODE_EXT;
}
if (enable_dynamic_depth_write) {
dynamic_states[ds_count++] = VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE;
}
VkPipelineDynamicStateCreateInfo dynamic_info{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = ds_count,
.pDynamicStates = dynamic_states
};
std::vector<VkVertexInputBindingDescription> vk_bindings;
vk_bindings.reserve(vertex_bindings.size());
for (const auto &b : vertex_bindings) {
vk_bindings.push_back({
.binding = b.binding,
.stride = b.stride,
.inputRate = b.is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
});
}
std::vector<VkVertexInputAttributeDescription> vk_attributes;
vk_attributes.reserve(vertex_attributes.size());
for (const auto &a : vertex_attributes) {
vk_attributes.push_back({
.location = a.location,
.binding = a.binding,
.format = vk::image_format_to_vk(a.format),
.offset = a.offset,
});
}
VkPipelineVertexInputStateCreateInfo vertex_input{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = (u32)vk_bindings.size(),
.pVertexBindingDescriptions = vk_bindings.empty() ? nullptr : vk_bindings.data(),
.vertexAttributeDescriptionCount = (u32)vk_attributes.size(),
.pVertexAttributeDescriptions = vk_attributes.empty() ? nullptr : vk_attributes.data(),
};
VkPipelineInputAssemblyStateCreateInfo input_assembly{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = vk::pipeline_topology_to_vk(topology),
};
VkPipelineRasterizationStateCreateInfo rasterizer{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = depth_clamp_enable ? VK_TRUE : VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = vk::pipeline_fill_mode_to_vk(fill_mode),
.cullMode = vk::pipeline_cull_mode_to_vk(cull_mode),
.frontFace = vk::triangle_winding_to_vk(winding),
.depthBiasEnable = depth_bias_enable ? VK_TRUE : VK_FALSE,
.depthBiasConstantFactor = depth_bias_constant,
.depthBiasClamp = depth_bias_clamp,
.depthBiasSlopeFactor = depth_bias_slope,
.lineWidth = 1.0f
};
VkPipelineMultisampleStateCreateInfo multisampling{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = vk::sample_count_to_vk(samples),
};
VkPipelineDepthStencilStateCreateInfo depth_stencil{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = depth_test_enable ? VK_TRUE : VK_FALSE,
.depthWriteEnable = depth_write_enable ? VK_TRUE : VK_FALSE,
.depthCompareOp = vk::pipeline_compare_op_to_vk(depth_compare_op),
};
VkPipelineViewportStateCreateInfo viewport_state{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 0,
.pViewports = nullptr,
.scissorCount = 0,
.pScissors = nullptr,
};
std::vector<VkFormat> vk_color_formats;
for (auto &format : color_attachment_formats) {
vk_color_formats.push_back(vk::image_format_to_vk(format));
}
VkPipelineRenderingCreateInfo rendering_info{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = (u32)color_attachment_formats.size(),
.pColorAttachmentFormats = vk_color_formats.empty() ? nullptr : vk_color_formats.data(),
.depthAttachmentFormat = vk::image_format_to_vk(depth_attachment_format),
};
VkPipelineColorBlendAttachmentState color_blend_attachment{
.blendEnable = blend_enable ? VK_TRUE : VK_FALSE,
.srcColorBlendFactor = vk::pipeline_blend_factor_to_vk(src_color_blend_factor),
.dstColorBlendFactor = vk::pipeline_blend_factor_to_vk(dst_color_blend_factor),
.colorBlendOp = vk::pipeline_blend_op_to_vk(color_blend_op),
.srcAlphaBlendFactor = vk::pipeline_blend_factor_to_vk(src_alpha_blend_factor),
.dstAlphaBlendFactor = vk::pipeline_blend_factor_to_vk(dst_alpha_blend_factor),
.alphaBlendOp = vk::pipeline_blend_op_to_vk(alpha_blend_op),
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT,
};
std::vector<VkPipelineColorBlendAttachmentState> blend_attachments(
rendering_info.colorAttachmentCount, color_blend_attachment
);
VkPipelineColorBlendStateCreateInfo color_blending{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = (u32)blend_attachments.size(),
.pAttachments = blend_attachments.data()
};
VkGraphicsPipelineCreateInfo pipeline_info = {VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
pipeline_info.pNext = &rendering_info;
pipeline_info.stageCount = (u32)shader_stages.size();
pipeline_info.pStages = shader_stages.data();
pipeline_info.pVertexInputState = &vertex_input;
pipeline_info.pInputAssemblyState = &input_assembly;
pipeline_info.pViewportState = &viewport_state;
pipeline_info.pRasterizationState = &rasterizer;
pipeline_info.pMultisampleState = &multisampling;
pipeline_info.pColorBlendState = &color_blending;
pipeline_info.pDepthStencilState = &depth_stencil;
pipeline_info.pDynamicState = &dynamic_info;
pipeline_info.layout = device->pipeline_layout;
VK_CHECK(vkCreateGraphicsPipelines(
device->logical, device->pipeline_cache, 1, &pipeline_info, nullptr, &result._handle
));
if (!name.empty()) {
char pipe_name[256];
(void)snprintf(pipe_name, sizeof(pipe_name), "pipe/%s", name.c_str());
VK_NAME_EX((*device), result._handle, pipe_name);
}
}
for (auto &stage : shader_stages) {
vkDestroyShaderModule(device->logical, stage.module, nullptr);
}
shader_modules.clear();
vertex_bindings.clear();
vertex_attributes.clear();
return result;
}
ComputePipeline ComputePipelineDesc::build() {
assert(device != nullptr && "ComputePipelineDesc::device must be assigned before build");
if (shader_modules.empty()) {
VEL_ERROR("Cannot build compute pipeline: no shader modules set");
return {};
}
VkShaderModule vk_mod = shader_modules[0].handle;
if (vk_mod == VK_NULL_HANDLE) {
VEL_ERROR("Cannot build compute pipeline: shader module is null");
return {};
}
VkPipelineShaderStageCreateInfo stage_info = {};
stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage_info.stage = static_cast<VkShaderStageFlagBits>(vk::shader_stage_to_vk(shader_modules[0].stage));
stage_info.module = vk_mod;
stage_info.pName = "main";
ComputePipeline result;
result.device = device;
VkComputePipelineCreateInfo pipeline_info = {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.stage = stage_info,
.layout = device->pipeline_layout,
};
VK_CHECK(
vkCreateComputePipelines(device->logical, device->pipeline_cache, 1, &pipeline_info, nullptr, &result._handle)
);
if (!name.empty()) {
char pipe_name[256];
(void)snprintf(pipe_name, sizeof(pipe_name), "pipe/%s", name.c_str());
VK_NAME_EX((*device), result._handle, pipe_name);
}
vkDestroyShaderModule(device->logical, stage_info.module, nullptr);
shader_modules.clear();
return result;
}
void destroy_pipeline(Device &device, RenderPipeline &p) {
vkDestroyPipeline(device.logical, p._handle, nullptr);
p = {};
}
void destroy_pipeline(Device &device, ComputePipeline &p) {
vkDestroyPipeline(device.logical, p._handle, nullptr);
p = {};
}
} // namespace rhi