Vulkan Pipeline
前言
前面我们对GPU Pipeline的发展史及其中的Graphics Pipeline、Computer pipeline、ray tracing pipeline做了一个较为全面的说明,并对其中涉及的逻辑及硬件部署、固定流水线模块的算法做了一个初步的探索,本章主要就软件栈上对pipeline的实现做一个梳理。
现如今,图形API众多,如D3D、OpenGL、Vulkan、CUDA等,在移动端较多的使用OpenGL es和Vulkan,OpenGL基于状态机(即所有经过OpenGL呈现出来的状态都需要由指令来改变,OpenGL接收指令输入,根据指令内容修改自身状态,完成输出,到达下一个状态,并保持到下一次指令输入),因此其流程偏于模板化,总是有着glEnable/glDisable/glColor*这种指令,这种固化式的编程对于新手而言,相对比较简单,尤其在对图形学有一定了解基础上,学习起来会更加迅速。而对于Vulkan而言,其灵活性更强,开发者在程序编写中的可介入性更强,可控性也更好。Vulkan的难度上虽较OpenGL大上很多,但这种恰到好处偏驱动式的编程在熟悉GPU工作原理情况下更加能得心应手。在移动端也有逐步取代OpenGL的趋势。
本文主要关注Graphics Pipeline在Vulkan中的细化,Computer pipeline偏向机器学习,不作为探讨的方向,ray tracing pipeline虽在游戏上有着隐隐想要取代Graphics pipeline趋势,但奈何性能不足及BVH硬件不成熟,当前的研究方向多集中于光栅化与ray tracing相结合,在前一章中对ray tracing的流程有做描述,后续也会基于Vulkan展开对该pipeline的编程,本节同样不作讨论。
虽然前面提到过Graphics Pipeline的一些细节,但为了便于理解,也会将之前的一些图例放到本章中便于理解概念。
Vulkan Pipeline的设计
- 提前构建Pipeline
在OpenGL中shader是在运行时去加载并编译进着色器程序,而一旦shader出现问题,在编译时不会被知晓,同时不同驱动对驱动的解释会存在不确定行为。此外,OpenGL中的状态基本是全局的,且状态配置来自于不同状态块数据,因此驱动程序通常需要保留所有状态副本,其次,Graphics Pipeline可编程阶段越来越多,每个input assember变化涉及到shader需要重新编译时都需要等到draw过程,这无不造成渲染性能的下降。
而vulkan将pipeline隔离,封装shader module,避免了在驱动程序中去编译微代码。但复杂状态的设置还是不可避免的需要重新编译shader,因此依赖开发者进行详细的计划部署,当然Vulkan也提供了Dynamic State使得部分状态可以动态变化而无需重新创建pipeline。
Pipeline构建
Pipeline的构建预设置图形管道中各模块状态,其创建出来的图形管道几乎不可变,因此当需要改变着色器、绑定不同帧缓冲区等时,需要重新创建pipeline,这使得对于不同的状态需要创建很多pipeline来管理,依赖于开发者对渲染中所有不同状态组合的覆盖,因此pipeline的创建是相当繁琐的。
Pipeline的创建主要以填充struct VkGraphicsPipelineCreateInfo完成,从该结构的定义来看,其包含的元素基本涵盖了前一章中所述的基础管线及tessellation。
// Provided by VK_VERSION_1_0
typedef struct VkGraphicsPipelineCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineCreateFlags flags;
uint32_t stageCount;
const VkPipelineShaderStageCreateInfo* pStages;
const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
const VkPipelineTessellationStateCreateInfo* pTessellationState;
const VkPipelineViewportStateCreateInfo* pViewportState;
const VkPipelineRasterizationStateCreateInfo* pRasterizationState;
const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
const VkPipelineColorBlendStateCreateInfo* pColorB