🗒️引擎开发日志1 - Vulkan封装
type
status
date
slug
summary
tags
category
icon
password
技术栈
- 底层图形API:Vulkan
- 引擎语言:C++
- 脚本语言:暂定Lua
- 引擎的设计模式:主体采用ECS+MVC
Vulkan Base
Context上下文
Swapchain交换链
RenderProcess渲染阶段的Pipline、Layout封装
Renderer渲染器
Shader着色器
CommandMgr命令管理器
Vec2、Vec4、Mat4数学库(暂定先实现这些)
Texture纹理
DescriptorMgr描述符
Manager
ShaderMgr:负责管理Shaders,目前暂时采用哈希表映射
TextureMgr:在原有的TextureMgr基础上增加材质的管理
ModelMgr:管理模型
Descriptor Problem
目前封装存在的最大的问题是不够独立,新的模块需要频繁更改封装好的Vulkan库的代码,需要进一步将公用接口进行隔离。
引入
假设两个Shader,不考虑frag和vert,那么可能会出现以下共用uniform的情况:
我们会发现,view、projection和worldTime是公用的全局变量,可能多个Shader都会用到它,所以他们的Descriptor应该是只有一个的,而不能每个Shader都拥有一个相同Descriptor的拷贝,不然当项目大了起来对内存来说是致命的。
我们需要引入一个管理这些uniform相关的数据结构。
原有的流程
Context创建Shader模块
这个示例中,首先获取了ShaderMgr,然后创建名字为default的shader,然后设置DescriptorSetLayoutBinding,分别为MVP矩阵和texture:
其中ShaderMgr关于SetDescriptorSetLayoutBinding和CreateDescriptorSetLayout的函数如下:
descriptorSetLayoutBindings是一个键值对,需要提供这个描述符的绑定点名称和所属于的集合。
descriptorSetLayouts是创建后的layout,通过集合名称确定。
RenderPass确定Layout
Renderer阶段获取更新set
理解Descriptor
如下图所示:
不像OpenGL,Vulkan不能对单独的Uniform或者其他被描述的变量进行Buffer。而是采用集合的设计,将要在Shader使用的Uniform变量的布局被SetLayout指定,通过
vk::DescriptorPool
分配出vk::DescriptorSet
,再由这个集合Buffer。这样做的效率比OpenGL高很多。还是原来的问题,考虑公共UBO,比如
view
和projection
,他们在渲染的过程中,只需要绑定一次即可。如果在OpenGL,每切换一个绘制对象就要重新绑定。所以很浪费效率。Solution
解决方案是分别封装DescriptorPool,DescriptorSetLayout,DescriptorWriter,每个类都会有各自的Builder。
DescriptorSetLayout
该类提供创建
vk::DescriptorSetLayout
的相应接口,能够接受多个多个binding。使用的方法如下:这说明创建了一个全局setlayout,描述了一个binding = 0的uniform对象:
可以设置多个binding,只需要:
描述了:
DescriptorPool
DescriptorWriter
FrameInfo Problem
现在存在一个很大的问题就是FrameInfo的封装可以说是基本没有:
可以看到,不同的RenderSystem,可能会用不同的FrameInfo,我们这样使用会让代码非常的丑,而且很冗余。
目前能想到的解决方案之一就是修改FrameInfo的结构体:
将原来的的set改成了map,就方便集成descriptorSet了。
不过大概率之后会改,因为总会遇到很复杂的情况。
UBO
在vulkan中uniform不能单独buffer到device,需要由descriptor set描述,而descirptor set需要由descriptor pool分配。
基于这个理念,结合之前封装的Descriptor组件,可以封装出一个这样的UBO模板类: