本文翻译自Qt Quick on Vulkan, Metal, and Direct3D
原文作者:Laszlo Agocs,Qt公司高级开发工程师
校审:Richard Lin
马上要发布Qt 5.14首个beta版了,是时候聊一聊其中一个重要新特性了。我们很难在一篇博文里分析所有图形技术栈和向Qt 6演进的演进细节,所以本文的第一、二部分将描述背景知识,并分析Qt 5.14中的新特性,然后在后续博文中深入探讨技术细节和未来发展方向。
Qt 5.14新功能页面提到:添加了独立于图形API的Scene Graph渲染引擎,这是第一个预览版而且是一个可选功能。这使得Qt Quick应用程序,除了OpenGL以外,还可以运行在Vulkan、Metal或Direct3D 11上。
Qt 6技术概览一文中说过,Qt 6的一个主要目标是避免在Qt的许多代码中直接使用OpenGL,通过适当的抽象来兼容更多的图形API,比如Vulkan、Metal和Direct3D。当然,OpenGL(和OpenGL ES)仍然是一种选择。这背后的主要动机不是提升性能,而是确保Qt Everywhere在未来仍然成立,未来可能运行在不支持或不推荐使用OpenGL的平台和设备上。同时,能有这样一套先进的、结构简单且清晰易懂的API还可以创造许多可能性,特别是对于那些需要提高性能的场景(比如,更少的API开销实现更低的CPU占用率),以及在Qt Quick背后的渲染引擎和其他模块中增加新的功能,比如最近公布的Qt Quick 3D。
此外,能够使用平台首选的、支持最好的图形API来渲染用户界面,这对于那些使用Qt来呈现UI,同时还用到原生API写的2D或3D图形的应用程序来说是个好消息。在这种应用中,一般Qt无法决定使用何种图形API:例如,一个macOS上的桌面应用程序希望使用Metal去渲染自己的3D内容,同时依靠Qt Quick去呈现2D UI元素,此时如果Qt Quick也使用Metal渲染会大有好处。对于那些过去关注Qt 5.x时代图形部分变化的人来说,这一点很熟悉:概念上,这和当时Qt Quick渲染引擎引入对OpenGL core profile上下文的操作的支持一样 —— Qt Quick本身并不需要关心这个,但是为了能够集成使用了core profile的外部渲染代码,Qt Quick必须顾及和它们协同的能力。所以从这个角度看,这是Qt 5的自然延续,现在扩展到OpenGL之外的其他图形API。
到这里,有两个显而易见问题:
对Qt所有(或至少大部分)有关图形渲染的部分进行全面的检查确实是Qt 6的目标。然而,停止支持Qt 5.x,然后试图设计、开发和重构一切,并指望一切都能变好,并不现实。正如Qt开发者过去、现在仍会提及的,任何API刚开始迭代时都可能不是最优的。因此,我们采用并行开发的方法,先专注于Qt中的一种特定的UI技术:Qt Quick。
Qt 5.14将发布使用全新渲染方法的Qt Quick预览版。默认情况下,不启用新的渲染方法,因此对应用程序来说不需要任何特殊的更改——会像在以往版本中一样,内部使用相同的直接基于OpenGL的渲染路径。但是,那些希望尝试新方法的人可以选择通过设置环境变量或使用main()中的C++ API启用新的渲染路径。我们希望尽早得到反馈,我们好继续迭代和演进,而不必等到Qt 6.0的发布。
阅读Qt 5.14文档简介,就会找到以下内容:
并不是所有的应用程序都能直接使用QSG_RHI环境变量运行。在Scene Graph节点中执行OpenGL调用、或在自定义材质中使用GLSL着色器代码定制的QQuickItem实现,就不能启用RHI渲染。这同样适用于带有GLSL代码的ShaderEffect元件。其实有更好的自定义材质和效果的办法,但是这些需要对应用程序进行移植。在Qt 5.14和Qt 5.15之间我们会尝试做一些适配,但是在Qt 6.0之前,不会进行大范围的推广和移植。另一方面,许多现有的QML应用程序应该可以正常工作,即使底层渲染引擎使用完全不同的API(如Vulkan或Metal)。
首先,需要说明的是,尽管不能总是实现开箱即用,使用API和着色器转换层(如MoltenVK、MoltenGL、ANGLE、Zink等)的可能性仍然存在。例如,MoltenVK允许基于Vulkan在macOS上渲染Qt Quick的UI。如果Qt Quick应用程序希望只使用Vulkan,并且仍然希望在macOS上运行,就也可以采用MoltenVK。(只要用户系统中安装了正确配置的Qt库,MoltenVK也已经就绪,那就可以运行)
把这个转换层变成一个强制性的依赖,并基于此将它和Qt一起部署,情况就完全不一样了。
因此,Qt没有采用依赖低级API转换程序的方法,而是自己定义了3D图形的高级抽象(内部使用,暂时不向应用程序开放)。抽象层的后端有许多针对特定API的实现,这和许多Qt组件的模式差不多。有些后端和操作系统平台是固定搭配(Metal,D3D),而其他后端可以对应多个平台(Vulkan,OpenGL)。有一个新开发的着色器管理单元来实现不同后台的切换,它用到了一些第三方库,比如glslang和SPIRV-Cross。更多细节将在之后的博文中详述。现在,让我们的视线转向稍高层面,看看它会给Qt 5.14中的Qt Quick带来什么。
让我们来看一个例子,即QUIt Coding著名的Qt5 Cinematic Experience演示程序。我们使用的是稍作修改的版本,其中少数Shader Effect Item被更新为同时可以使用两种Qt Quick Scene Graph渲染路径的格式。该版本可在这里找到。
正常启动应用程序,设置QSG_INFO=1,得到:
就像在调试输出中打印的日志一样,这是在Linux桌面的OpenGL上运行的:
qt.scenegraph.general: threaded render loop
qt.scenegraph.general: Using sg animation driver
qt.scenegraph.general: Animation Driver: using vsync: 16.95 ms
qt.scenegraph.general: opengl texture atlas dimensions: 2048x1024
qt.scenegraph.general: GL_VENDOR: X.Org
qt.scenegraph.general: GL_RENDERER: AMD Radeon (TM) R9 M360 (VERDE, DRM 3.23.0, 4.15.0-62-generic, LLVM 8.0.1)
qt.scenegraph.general: GL_VERSION: 4.5 (Compatibility Profile) Mesa 19.2.0-devel (git-08f1cef 2019-07-25 bionic-oibaf-ppa)
qt.scenegraph.general: GL_EXTENSIONS: ...
qt.scenegraph.general: Max Texture Size: 16384
qt.scenegraph.general: Debug context: false
如果我们设置QSG_RHI=1,会发生什么变化?
qt.scenegraph.general: Using QRhi with backend OpenGL
graphics API debug/validation layers: 0
QRhi profiling and debug markers: 0
qt.scenegraph.general: threaded render loop
qt.scenegraph.general: Using sg animation driver
qt.scenegraph.general: Animation Driver: using vsync: 16.95 ms
qt.rhi.general: Created OpenGL context QSurfaceFormat(version 4.5, options QFlags<QSurfaceFormat::FormatOption>(DeprecatedFunctions), depthBufferSize 24, redBufferSize 8, greenBufferSize 8, blueBufferSize 8, alphaBufferSize 0, stencilBufferSize 8, samples -1, swapBehavior QSurfaceFormat::DoubleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile QSurfaceFormat::CompatibilityProfile)
qt.rhi.general: OpenGL VENDOR: X.Org RENDERER: AMD Radeon (TM) R9 M360 (VERDE, DRM 3.23.0, 4.15.0-62-generic, LLVM 8.0.1) VERSION: 4.5 (Compatibility Profile) Mesa 19.2.0-devel (git-08f1cef 2019-07-25 bionic-oibaf-ppa)
qt.scenegraph.general: MSAA sample count for the swapchain is 1. Alpha channel requested = no.
qt.scenegraph.general: rhi texture atlas dimensions: 2048x1024
乍一看差别不大。它似乎还在使用OpenGL。然而,内部没有直接使用OpenGL,也没有Qt Quick Scene Graph中各处穿插的GLSL着色器源码。相反,会使用QRhi(即QtGui模块中的一个私有API,全称为Qt Rendering Hardware Interface)进行渲染。
让我们把它变得更有趣一点。设置QSG_RHI_BACKEND=vulkan:
qt.scenegraph.general: Using QRhi with backend Vulkan
graphics API debug/validation layers: 0
QRhi profiling and debug markers: 0
qt.scenegraph.general: threaded render loop
qt.scenegraph.general: Using sg animation driver
qt.scenegraph.general: Animation Driver: using vsync: 16.95 ms
WARNING: radv is not a conformant vulkan implementation, testing use only.
qt.rhi.general: Physical device 0: 'AMD RADV CAPE VERDE (LLVM 8.0.1)' 19.1.99
qt.rhi.general: using this physical device
qt.rhi.general: queue family 0: flags=0xf count=1
qt.rhi.general: queue family 1: flags=0xe count=2
qt.rhi.general: 55 device extensions available
qt.scenegraph.general: MSAA sample count for the swapchain is 1. Alpha channel requested = no.
qt.scenegraph.general: rhi texture atlas dimensions: 2048x1024
qt.rhi.general: Creating new swapchain of 3 buffers, size 1280x720, presentation mode 2
处理的很好。显然它现在正在通过Vulkan渲染。甚至更奇异的Qt Quick特性,如distance field text渲染、着色器效果和粒子效果都如预期的那样存在。
在RenderDoc中运行应用程序并捕获帧,结果如下所示。Qt Quick确实正在构建Vulkan管道状态对象和命令缓冲区,着色器代码以SPIR-V字节码格式提供。
就这些了。在本系列的第二篇博文中,我们将研究Qt 5.14为macOS和Windows提供了什么。在那之后,我们将继续研究所有这些是如何在底层工作的,以及对需要自定义材质和特效的应用程序有什么影响。
期待激动人心的时刻吧!