图形管道创建–对比计算管线
创建Graphics pipeline
首先在VulkanEngine 添加了成员变量和函数
VkPipelineLayout _trianglePipelineLayout; // 创建管道要用到pipeline layout 没错吧
VkPipeline _trianglePipeline;
void init_triangle_pipeline();
void draw_geometry(VkCommandBuffer cmd);
然后看一下 init_triangle_pipeline
的实现
void VulkanEngine::init_triangle_pipeline() {
VkShaderModule triangleFragShader;
//
// 加载需要用到的fragment shader 和 vertexShader
//
if (!vkutil::load_shader_module("../shaders/colored_triangle.frag.spv", _device, &triangleFragShader)) {
fmt::print("Error when building the triangle fragment shader module");
}
else {
fmt::print("Triangle fragment shader succesfully loaded");
}
VkShaderModule triangleVertexShader;
if (!vkutil::load_shader_module("../shaders/colored_triangle.vert.spv", _device, &triangleVertexShader)) {
fmt::print("Error when building the triangle vertex shader module");
}
else {
fmt::print("Triangle vertex shader succesfully loaded");
}
// 创建pipelineLayout 控制Shader的输入输出
// 这里没有使用 descriptor sets, 基本上都是默认值
VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info();
VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_trianglePipelineLayout));
PipelineBuilder pipelineBuilder;
//设置pipeline layout
pipelineBuilder._pipelineLayout = _trianglePipelineLayout;
//设置vertex shader 和 fragment shader
pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader);
// 设置拓扑为三角形
pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
// 画实心的三角形而不是线框的
pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL);
// 不做背部剔除
pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
// 不用msaa
pipelineBuilder.set_multisampling_none();
// 不混合
pipelineBuilder.disable_blending();
// 不做深度测试
pipelineBuilder.disable_depthtest();
// 设置color attachment的格式
pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat);
pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED);
//finally build the pipeline
_trianglePipeline = pipelineBuilder.build_pipeline(_device);
//clean structures
vkDestroyShaderModule(_device, triangleFragShader, nullptr);
vkDestroyShaderModule(_device, triangleVertexShader, nullptr);
_mainDeletionQueue.push_function([&]() {
vkDestroyPipelineLayout(_device, _trianglePipelineLayout, nullptr);
vkDestroyPipeline(_device, _trianglePipeline, nullptr);
});
}
创建pipeline layout 没有用描述符集,基本都是默认值,对比一下计算管线的layout
计算管线设置了 pSetLayouts ,同时使用了 pushConstant
VkPipelineLayoutCreateInfo computeLayout{};
computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
computeLayout.pNext = nullptr;
// 计算管线设置了 pSetLayouts
computeLayout.pSetLayouts = &_drawImageDescriptorLayout;
computeLayout.setLayoutCount = 1;
// pushConstant
VkPushConstantRange pushConstant{};
pushConstant.offset = 0;
pushConstant.size = sizeof(ComputePushConstants) ;
pushConstant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
computeLayout.pPushConstantRanges = &pushConstant;
computeLayout.pushConstantRangeCount = 1;
VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout));
然后具体看一下pipeline builder的实现
定义
class PipelineBuilder {
public:
std::vector<VkPipelineShaderStageCreateInfo> _shaderStages;
// 是vector,说明可以有多个VkPipelineShaderStageCreateInfo,看了一下发现其实就是创建shader
VkPipelineInputAssemblyStateCreateInfo _inputAssembly;
VkPipelineRasterizationStateCreateInfo _rasterizer;
VkPipelineColorBlendAttachmentState _colorBlendAttachment;
VkPipelineMultisampleStateCreateInfo _multisampling;
VkPipelineLayout _pipelineLayout;
VkPipelineDepthStencilStateCreateInfo _depthStencil;
VkPipelineRenderingCreateInfo _renderInfo;
VkFormat _colorAttachmentformat;
PipelineBuilder(){ clear(); }
void clear();
void set_input_topology(VkPrimitiveTopology topology);
void set_polygon_mode(VkPolygonMode mode);
void set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace);
void set_multisampling_none();
void disable_blending();
void set_color_attachment_format(VkFormat format);
void set_depth_format(VkFormat format);
void disable_depthtest();
void set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader);
VkPipeline build_pipeline(VkDevice device);
};
实现
void PipelineBuilder::clear()
{
//clear all of the structs we need back to 0 with their correct stype
_inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
_rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
_colorBlendAttachment = {};
_multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
_pipelineLayout = {};
_depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
_renderInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
_shaderStages.clear();
}
VkPipeline PipelineBuilder::build_pipeline(VkDevice device) {
// 设置 viewport 和scissor,好像这些create info 都是 都是state create info
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.pNext = nullptr;
viewportState.viewportCount = 1;
viewportState.scissorCount =1;
// 不混合,因为现在还没有用透明物体
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.pNext = nullptr;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &_colorBlendAttachment;
//completely clear VertexInputStateCreateInfo, as we have no need for it
VkPipelineVertexInputStateCreateInfo _vertexInputInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
// 正式写Graphics pipeline create info
VkGraphicsPipelineCreateInfo pipelineInfo = {.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
//connect the renderInfo to the pNext extension mechanism
pipelineInfo.pNext = &_renderInfo;
// 很多信息都是在例如disable blend这种函数里填充的,这里简单设置一下就好
pipelineInfo.stageCount = (uint32_t)_shaderStages.size();
pipelineInfo.pStages = _shaderStages.data();
pipelineInfo.pVertexInputState = &_vertexInputInfo;
pipelineInfo.pInputAssemblyState = &_inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &_rasterizer;
pipelineInfo.pMultisampleState = &_multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDepthStencilState = &_depthStencil;
pipelineInfo.layout = _pipelineLayout;
// Dynamic state,我暂时不懂这个
VkDynamicState state[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamicInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
dynamicInfo.pDynamicStates = &state[0];
dynamicInfo.dynamicStateCount = 2;
pipelineInfo.pDynamicState = &dynamicInfo;
// its easy to error out on create graphics pipeline, so we handle it a bit
// better than the common VK_CHECK case
VkPipeline newPipeline;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo,
nullptr, &newPipeline)
!= VK_SUCCESS) {
fmt::println("failed to create pipeline");
return VK_NULL_HANDLE; // failed to create graphics pipeline
} else {
return newPipeline;
}
}
void PipelineBuilder::set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader)
{
_shaderStages.clear();
_shaderStages.push_back(
vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_VERTEX_BIT, vertexShader));
_shaderStages.push_back(
vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader));
}
void PipelineBuilder::set_input_topology(VkPrimitiveTopology topology)
{
_inputAssembly.topology = topology;
// we are not going to use primitive restart on the entire tutorial so leave
// it on false
_inputAssembly.primitiveRestartEnable = VK_FALSE;
}
void PipelineBuilder::set_polygon_mode(VkPolygonMode mode)
{
_rasterizer.polygonMode = mode;
_rasterizer.lineWidth = 1.f;
}
void PipelineBuilder::set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace)
{
_rasterizer.cullMode = cullMode;
_rasterizer.frontFace = frontFace;
}
void PipelineBuilder::set_multisampling_none()
{
_multisampling.sampleShadingEnable = VK_FALSE;
// multisampling defaulted to no multisampling (1 sample per pixel)
_multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
_multisampling.minSampleShading = 1.0f;
_multisampling.pSampleMask = nullptr;
//no alpha to coverage either
_multisampling.alphaToCoverageEnable = VK_FALSE;
_multisampling.alphaToOneEnable = VK_FALSE;
}
void PipelineBuilder::disable_blending()
{
//default write mask
_colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
//no blending
_colorBlendAttachment.blendEnable = VK_FALSE;
}
void PipelineBuilder::set_color_attachment_format(VkFormat format)
{
_colorAttachmentformat = format;
//connect the format to the renderInfo structure
_renderInfo.colorAttachmentCount = 1;
_renderInfo.pColorAttachmentFormats = &_colorAttachmentformat;
}
void PipelineBuilder::set_depth_format(VkFormat format)
{
_renderInfo.depthAttachmentFormat = format;
}
void PipelineBuilder::disable_depthtest()
{
_depthStencil.depthTestEnable = VK_FALSE;
_depthStencil.depthWriteEnable = VK_FALSE;
_depthStencil.depthCompareOp = VK_COMPARE_OP_NEVER;
_depthStencil.depthBoundsTestEnable = VK_FALSE;
_depthStencil.stencilTestEnable = VK_FALSE;
_depthStencil.front = {};
_depthStencil.back = {};
_depthStencil.minDepthBounds = 0.f;
_depthStencil.maxDepthBounds= 1.f;
}
画三角形
先看一下用来画三角形的代码吧
void VulkanEngine::draw_geometry(VkCommandBuffer cmd) {
// 创建RenderAttachment
VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_GENERAL);
VkRenderingInfo renderInfo = vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr);
// 开始render
vkCmdBeginRendering(cmd, &renderInfo);
// 绑定pipeline
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _trianglePipeline);
// 设置动态view port 和 scissors
VkViewport viewport = {};
viewport.x = 0;
viewport.y = 0;
viewport.width = _drawExtent.width;
viewport.height = _drawExtent.height;
viewport.minDepth = 0.f;
viewport.maxDepth = 1.f;
vkCmdSetViewport(cmd, 0, 1, &viewport);
VkRect2D scissor = {};
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent.width = _drawExtent.width;
scissor.extent.height = _drawExtent.height;
vkCmdSetScissor(cmd, 0, 1, &scissor);
//launch a draw command to draw 3 vertices
// draw 命令! 参数是VkCommandBuffer, vertex count, instance count, first vertex, first instance
vkCmdDraw(cmd, 3, 1, 0, 0);
vkCmdEndRendering(cmd);
}
看一下调用draw_geometry的地方
// 调用之前先从 VK_IMAGE_LAYOUT_GENERAL 转成 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
// 这和计算管线直接用VK_IMAGE_LAYOUT_GENERAL不同
vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
draw_geometry(cmd);
// 画完之后转成VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,把swapchain的图像转成VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
// 一个是transfer src 一个是transfer dst
vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
总结
图形管线看上去就是设置我这次绘制的状态,例如,要不要做颜色混合,要不要深度测试,要用那些shader
绘制到image之前,要吧image变成VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL(不用也行,这个得等我深刻理解vulkan了)