阶段总结
现在,让我们回忆之下我们为了创建图形管线而创建的对象:
-
着色器阶段:定义了着色器模块用于图形管线哪一可编程阶段
-
固定功能状态:定义了图形管线的固定功能阶段使用的状态信息,比如输入装配,视口,光栅化,颜色混合
-
管线布局:定义了被着色器使用,在渲染时可以被动态修改的uniform变量
-
渲染流程:定义了被管线使用的附着附着的用途
上面所有这些信息组合起来完整定义了图形管线的功能,现在,我们可以开始填写VkGraphicsPipelineCreateInfo结构体来创建管线对象。我们需要在createGraphicsPipeline函数的尾部,vkDestroyShaderModule函数调用之前添加下面的代码:
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
上面代码我们通过指定VkPipelineShaderStageCreateInfo结构体数组来引用之前我们创建的两个着色器阶段。
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr; // Optional
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr; // Optional
接着,我们引用了之前设置的固定功能阶段信息。
pipelineInfo.layout = pipelineLayout;
指定之前创建的管线布局。
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
最后,引用之前创建的渲染流程对象和图形管线使用的子流程在子流程数组中的索引。在之后的渲染过程中,仍然可以使用其它与这个设置的渲染流程对象相兼容的渲染流程。
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipelineInfo.basePipelineIndex = -1; // Optional
basePipelineHandle和basePipelineIndex成员变量用于以一个创建好的图形管线为基础创建一个新的图形管线。当要创建一个和已有管线大量设置相同的管线时,使用它的代价要比直接创建小,并且,对于从同一个管线衍生出的两个管线,在它们之间进行管线切换操作的效率也要高很多。我们可以使用basePipelineHandle来指定已经创建好的管线,或是使用basePipelineIndex来指定将要创建的管线作为基础管线,用于衍生新的管线。目前,我们只使用一个管线,所以将这两个成员变量分别设置为VK_NULL_HANDLE和-1,不使用基础管线衍生新的管线。这两个成员变量的设置只有在VkGraphicsPipelineCreateInfo结构体的flags成员变量使用了VK_PIPELINE_CREATE_DERIVATIVE_BIT标记的情况下才会起效。
现在,让我们添加一个VkPipeline成员变量来存储创建的管线对象:
VkPipeline graphicsPipeline;
创建管线对象:
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
throw std::runtime_error("failed to create graphics pipeline!");
}
vkCreateGraphicsPipelines函数的参数要比一般的Vulkan的对象创建函数的参数多一些。它被设计成一次调用可以通过多个VkGraphicsPipelineCreateInfo结构体数据创建多个VkPipeline对象。
vkCreateGraphicsPipelines函数第二个参数可以用来引用一个可选的VkPipelineCache对象。通过它可以将管线创建相关的数据进行缓存在多个vkCreateGraphicsPipelines函数调用中使用,甚至可以将缓存存入文件,在多个程序间使用。使用它可以加速之后的管线创建,我们会在之后的章节对它进行更为详细地说明。
我们需要在应用程序结束前,清除我们创建的管线对象:
void cleanup() {
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
...
}
现在,编译运行程序,确保一切正常。到此,我们就成功创建了一个图形管线,接下来只需要设置好帧缓冲,提交绘制指令,就可以在屏幕上看到渲染的图像了。
本章节代码:
C++:
https://vulkan-tutorial.com/code/12_graphics_pipeline_complete.cpp
Vertex Shader:
https://vulkan-tutorial.com/code/09_shader_base.vert
Fragment: