十一月 30, 2019
Comments
本文翻译自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提供了什么。在那之后,我们将继续研究所有这些是如何在底层工作的,以及对需要自定义材质和特效的应用程序有什么影响。
期待激动人心的时刻吧!
Download the latest release here: www.qt.io/download
Qt 6.11 is now available, with new features and improvements for application developers and device creators.
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.