Vulkan与OpenGL的对比
传统图形API与现代图形API
传统图形API指的是OpenGL/DirectX11这类简单易用、驱动托管严重的图形接口;而现代图形API则指的是Vulkan/Metal/DirectX12这类使用复杂、暴露更多底层硬件功能来显式控制的弱驱动设计的图形接口。
现代图形API与传统图形API相比,使用和设计上有不少的区别,下面总结一些Vulkan与OpenGL的区别来对比说明。
渲染状态管理
OpenGL是一个全局的状态机,而Vulkan提供了PSO(PipelineState Object)来保存不同的管线状态。这样的区别是:
驱动实现
OpenGL由于要在驱动内管理和保存全局的状态,实现复杂。而Vulkan将状态管理暴露给应用层管理,不再需要在驱动层处理,从而驱动实现更为简单。
状态切换性能
OpenGL由于是全局状态机,在渲染时候经常需要设置和恢复状态,都会触发驱动内对全局状态机的管理,容易引发CPU上的性能瓶颈。但是Vulkan的PSO可以进行预编译,将不同的状态提前存储到不同的PSO上,同时对PSO收集离线缓存和提前预加载编译,等到渲染时候进行PSO切换即可。这样可以大幅度降低因为渲染状态切换导致的CPU性能瓶颈。
同时,Vulkan的渲染状态是缓存在PSO内,相比OpenGL的全局状态管理,可能性能本身就更好。
多线程支持
OpenGL只支持单线程提交DC,而Vulkan支持多线程提交DC。
具体思路:Vulkan支持多线程录制command后再提交到command queue。比如,每个线程创建一个command buffer,并行录制command到command buffer,再提交到指定的command queue。
更进一步,Vulkan支持多个command queue,这些queue可能在硬件层(驱动内)是并行的,比如Graphics的queue和compute的queue。
Shader编译
OpenGL将Shader的编译直接交给驱动负责。而Vulkan则将Shader使用spirv-tools编译成跨平台的中间语言spir-v,驱动只负责编译spir-v。这样的好处是:
- 跨平台:驱动只需要处理中间格式,方便使用工具将不同的语言比如hlsl、glsl等都统一编译成spirv。
- 性能:驱动只需要编译中间语言,不需要对着色器代码做负责的编译检查语法分析等,性能更好。
资源绑定
OpenGL的资源绑定都是一次API调用,比如glBindTexture等。而Vulkan支持在PSO上一次性绑定多个资产,以及Bindless等。具体可以参考文章系列:游戏引擎随笔 0x13:现代图形 API 的 Bindless,游戏引擎随笔 0x23:再论现代图形 API 的 Bindless(上),游戏引擎随笔 0x24:再论现代图形 API 的 Bindless(下)。
内存管理
OpenGL不支持显式的内存管理,都是托管给驱动负责。但是Vulkan支持或者说必须显式的管理与分配内存,因为驱动不再负责。具体来说是,Vulkan必须应用层申请内存同时进行管理,而OpenGL不需要。
// Vulkan 显式分配显存
VkMemoryAllocateInfo allocInfo{};
allocInfo.allocationSize = size;
allocInfo.memoryTypeIndex = findMemoryType(...);
vkAllocateMemory(device, &allocInfo, nullptr, &deviceMemory);
vkBindBufferMemory(device, buffer, deviceMemory, 0);// OpenGL 隐式分配
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
比如上述代码,OpenGL是直接调用glGenBuffers获得buffer的id即可,内部由驱动去申请和管理内存;而Vulkan则需要调用vkAllocateMemory具体的申请内存,后续的管理和同步也需要应用层负责。因此,理论上Vulkan可以实现更接近应用需要的内存管理机制。
同步机制
这里的同步包括CPU和GPU的同步,以及GPU不同的渲染操作之间的同步等。OpenGL的同步都由驱动隐式完成,应用层无法控制。而Vulkan提供了多种手段来显式控制同步,以更好的优化性能。
- Fence用于CPU和GPU之间的同步,比如CPU等待某个GPU操作完成。
- Semaphores用GPU内部的同步。
- Pipeline Barrier用于同一个queue内同步。
- Event可以用于等待Pipeline事件发生,也可以用于CPU和GPU之间的同步。
更具体的内容可以参考文章:游戏引擎随笔 0x07:现代图形 API 的同步。
错误验证
OpenGL由于是驱动管理全局的状态,而且驱动内置了错误管理和验证机制,导致驱动的实现复杂并且有较大的性能损失。相反,Vulkan的错误验证层是可选的组件,并且在Vulkan层,并没有在硬件的驱动内实现;在开发阶段,可以开启验证层排查问题,而真正的运行阶段并没有验证层。
- 性能:Vulkan没有错误验证层性能更好,驱动实现更简单。
- 开发:Vulkan由于没有驱动兜底,因此开发难度更大,需要更仔细的调试和验证。
适合场景
以上这些对比区别,都是CPU层面的,因此Vulkan这种现代图形API适合需要对CPU性能做极致优化的场景。如果,性能瓶颈在GPU上,将图形API从OpenGL切换到Vulkan上也无法解决问题。相信随着硬件的发作,Vulkan会逐渐替代OpenGL/OpenGLES成为事实上的默认跨平台图形API。