这一节来讨论 Vulkan 中的显示与绘制机制,过程中可以加深对 SwapChain、Renderpass、Subpass 和 FrameBuffer 的理解。
1 SwapChains
因为支持 Vulkan 运行的平台有很多,有些以计算为目的应用也不需要把绘制的结果呈现给用户,所以显示相关的 API 并不是 Vulkan 核心 API 的一部分,而是通过扩展(extensions)的方式提供。
Vulkan 没有默认帧缓冲的概念,它需要一个能够缓冲渲染操作的组件。在 Vulkan 中,这一组件就是交换链。Vulkan 的交换链必须显式地创建,不存在默认的交换链。交换链本质是一个包含了若干等待呈现的图像的队列。我们的应用程序从交换链获取一张图像,然后在图像上进行渲染操作,完成后,将图像返回到交换链的队列中。交换链的队列的工作方式和它呈现图像到表面的条件依赖于交换链的设置。但通常来说,交换链被用来同步图像呈现和屏幕刷新。
每一个支持显示的平台都有自己的窗口系统 Windowing System Integration (WSI)。Vulkan提供了 Swap Chains 跟平台的窗口系统对接。Swap Chains 会向窗口系统申请一个或者多个可以用于显示的 image 对象。我们可以将这些 image 对象作为我们的绘制目标,绘制完成后通过 vkQueuePresentKHR()
函数送显。
Presentation(送显)功能依赖于平台提供支持,不同平台的特性有所差异。我们在通过Swap Chains 获取用来显示的image object之后,还需要确认当前平台支持的显示模式。有些平台可能支持多种显示模式,我们需要根据自己的场景做出选择。
Vulkan API中定义了四种显示模式:
- IMMEDIATE:送显之后立刻显示,替换当前正在显示的图像,不会关心 vsync。显示引擎中也没有等待队列保存图像。这种方式不在消隐期间替换图像,很容易被用户看到撕裂现象。
- FIFO:每个 Vulkan API 的实现都必须支持 FIFO 模式。这种模式下,送显的图像会先被保存在一个先进先出的队列中,当 vsync 触发时,队列里的图像才会替换当前显示的图像,被替换的图像可以重新被应用程序申请绘制。
- FIFO RELAXED:在 FIFO 上做了修改,当图像的显示时长超过一个 vsync 周期时,下一个图像不会再等待下一次 vsync 的到来,而是立刻显示。如果这个操作在非消隐期间,用户可以看到撕裂现象。
- MAILBOX:该模式下,图像仅仅在消隐期间显示,所以用户看不到撕裂现象。在显示引擎内部,只使用单个元素的队列,当有新的图像被生成时,等待队列的图像将被替换。所以这种模式下,显示的图像永远是最新的。
2 Renderpass 和 Framebuffer
在 Vulkan 中,绘制命令被组织成 Render Pass。Renderpass 是一组 subpass 的集合,每个 subpass 描述的是如何使用 color attachments 等图像资源。Renderpass 管理着 subpass 之间的依赖关系和绘制顺序。
FrameBuffer 是一系列 Image 对象的集合,它是所有绘制操作的最终目标。Swap Chains 从窗口系统申请来的 Image 对象跟 FrameBuffer 绑定以后,绘制操作才能绘制到这些 Image 上面。
如果把绘制流程比作绘画:
Swap Chains 从窗口系统申请来的 Image 是用来作画的纸张;
FrameBuffer 就是一个可以夹住多张纸的画板;
Renderpasses 则表示每张纸要画什么,他们之间的绘制顺序是什么样子的,绘制时是不是要参考前一张的绘制内容。
当绘制完成后,我们就可以把 Image 传给平台的窗口系统进行显示了。