opengl相对与vulkan不是一个级别的东西,opengl几十行代码就可以绘制一个简单的物体,而vulkan需要几百行才能绘制,所以vulkan的学习曲线比较陡峭,本人经过也经过一个很长的时间的断断续续学习对vulkan有了一些认识,在这里做一下笔记,错误请指出。
首先针对vulkan的上下文进行讲解,vulkan对于底层的整个管线流程做了开放,并且给出了很多逻辑上的对外接口,基本上下文包括Instance、physicalDevice、logicDevice、Queue、surface、swapchain、Image、ImageView、pipeline、CommandPool、CommandBuffer、RenderPass、PipelineVertexInputState、push contants、Layout。
Instance存在的意义,记得很早看过dx12的api,都有类似的功能,Instance就好比一个总管,可以设置一写全局的东西进去,如验证层和扩展(物理设备也有自己单独的验证层和扩展),可以枚举很多物理设备,是操作系统层面管理所有显卡设备的一个逻辑功能,可以枚举出所有的显卡,电脑上一般都由集成显卡和独立显卡,所以在选择显卡的时候Instance会枚举出所有的显卡,然后拿到每个显卡的功能,看看哪些显卡满足我们的使用要求就使用哪个显卡。
physicDevice是计算机上显卡硬件的代理(类似显卡驱动的东西),显卡支持的功能就是真正能用的功能,所以很多的信息需要从physicDevice去查询(例如队列簇等信息)。
logicDevice存在的意义,本人认为是选择了physicalDevice中的某些功能进行封装,而不是直接代理physicalDevice,比如对于显卡有很多的命令队列簇(Queue Family 0,Queue Family 1,Queue Family 2),而每个Queue Family中又包含了不同的命令队列。而在创建logicDevice的时候会设置这个logicDevice使用那些命令队列簇,不设置的不会用到,就好比显卡设备有很多的功能,而我们的渲染器只是需要一部分功能,那就把一部分功能拿出来打包成一个logicDevice去使用就好了。
Queue,既然logicDevice已经封装了队列信息,那么就可以直接从logicDevice中取出Queue,方便后续的命令缓冲传入Queue中来绘制命令(Graphic)、计算命令(Compute)、传输数据到GPU(transfer),当然命令队列是一个队列,与cpu共享访问权限,就会涉及到冲突问题,就要加锁,就会出现信号量、屏障等。
surface是屏幕格式的一个接口,通过surface可以知道显示器图像的格式。
Swapchain,交换链不属于显卡的功能,是用于显示的,在显卡与屏幕之间需要一些协商的工作,就是交换链的功能做(不显示图像就不用交换链),交换链根据surface的一些格式(大小、颜色格式【颜色空间】)跟显卡进行协商,同时交换链也设定创建多少张这样的图像,通常用于创建双缓冲、三缓冲。还有一个重要的应用就是交换链连接图像渲染队列和显示队列,当渲染一张图像时,先从交换链中取出一张图像作为当作帧缓冲附件进行渲染,渲染完成后将这个图像传输到显示队列中,用来显示图像到屏幕上。
Image,图像是交换链中创建的,就需要在交换链中读取出来。
ImageView,Image是一个内存块(或者一种不为我们知道的格式存在),如果我们想读取这块内存,就要对这个Image进行格式化,ImageView就是一个格式化的过程。
pipeline,这是显卡对图元的一次渲染流程,包含了从顶点装配、顶点着色器、视口设置、光栅化、像素着色器、深度模板比较、混合等各个阶段的参数设置,这个管线基本上是一个整体,不能中途更改(除了线宽、视口大小),也就是说一个着色器glsl代码对应一个pipeline。
CommandPool,就是一个命令池,和很多的池化概念一样,池化就是需要的时候在池中取出,不用的时候放到池子中去,避免创建、销毁过程中的性能损失。
CommandBuffer命令,通常情况下命令与swapchain中缓冲的个数相同(双缓冲或者三缓冲),因为如果是三缓冲,同一个帧(同一幅画面)肯定在同一个GraphicQueue中被调度渲染,其他GraphicQueue肯定是闲置的,所以在开始渲染一帧时首先看看swapchain中哪个缓冲是闲置的,这个闲置的索引也对应了哪个GraphicQueue是闲置的,就将CommandBuffer添加到这个闲置的GraphicQueue中,一帧中有多个渲染实体(一个实体对应一组vertexBuffer、Uniform、pipeline)时,需要使用这个命令一次封装一个实体,实体被一个一个的提交到命令队列中。
RenderPass,这是一个对渲染过程中帧缓冲的描述,或者是对帧缓冲的布局进行说明,为什么在pipeline中需要使用这个RenderPass呢,很多资料中给出的解释是方便vulkan在pipeline的fragmentshader输出图象时进行优化,给出这样的解释让我很是不解(就好比解释不了就推给神或者上帝),同时vulkan的设计初衷是让程序掌握很多底层的细节,这个细节处理的不好。renderpass的创建信息是这样的,renderpass中包含了一组对各个帧附件的描述,这个原理我还没有完全弄明白,留待后面修改。
PipelineVertexInputState 这个布局对应了shader中顶点属性的布局,与opengl中顶点属性布局的功能对应。
push contants 直接将常量数据通过命令缓冲区传递给着色器,而不是通过内存传递,
常量的限制是只能在命令录制中传递,同时又大小的限制(vulkan规定128字节),这里最重要的点是常量的对齐,vec3内存是与vec4的内存一样对齐的是16字节对齐。
Layout是数据的布局,这个布局主要是针对推送常量和uniform数据的内存格式使用的。